Remember that the biggest advantage you get from using a map[string]interface{} map, or any map that stores an interface{} value in general, is that you still have your data in its original state and data type.
Nowadays, web services work by exchanging JSON records. If you get a JSON record in an anticipated format, then you can process it as expected and everything will be fine. However, there are times when you might get an erroneous record or a record in an unsupported JSON format. In these cases, using map[string]interface{} for storing these unknown JSON records (arbitrary data) is a good choice because map[string]interface{} is good at storing JSON records of unknown types.
package main import ( "encoding/json" "fmt" "os" ) var JSONrecord = `{ "Flag": true, "Array": ["a","b","c"], "Entity": { "a1": "b1", "a2": "b2", "Value": -456, "Null": null }, "Message": "Hello Go!" }` func typeSwitch(m map[string]interface{}) { for k, v := range m { switch c := v.(type) { case string: fmt.Println("Is a string!", k, c) case float64: fmt.Println("Is a float64!", k, c) case bool: fmt.Println("Is a Boolean!", k, c) case map[string]interface{}: fmt.Println("Is a map!", k, c) typeSwitch(v.(map[string]interface{})) default: fmt.Printf("...Is %v: %T!\n", k, c) } } } func exploreMap(m map[string]interface{}) { for k, v := range m { embMap, ok := v.(map[string]interface{}) // If it is a map, explore deeper if ok { fmt.Printf("{\"%v\": \n", k) exploreMap(embMap) fmt.Printf("}\n") } else { fmt.Printf("%v: %v\n", k, v) } } } func main() { if len(os.Args) == 1 { fmt.Println("*** Using default JSON record.") } else { JSONrecord = os.Args[1] } JSONMap := make(map[string]interface{}) err := json.Unmarshal([]byte(JSONrecord), &JSONMap) if err != nil { fmt.Println(err) return } exploreMap(JSONMap) fmt.Println("------------------------------------------------") typeSwitch(JSONMap) }