golang: Encode/Decode arbitrary JSON
Just a quick post in case I forget how to do this in future. I had a use case for working with reading arbitrary JSON from a file and parsing it using Golang, which meant that the recommended way of using structs wasn’t suitable.
json.Marshal
The first thing to do was marshal a custom data structure into being a JSON string using json.Marshal. This was fairly straightforward to do:
cacheContent := map[string]interface{}{
"foo": "bar",
"baz": map[string]interface{}{
"bee": "boo",
},
}
str, err := json.Marshal(cacheContent)
if err != nil {
fmt.Println("Error encoding JSON")
return
}
fmt.Println(string(str))
# => {"baz":{"bee":"boo"},"foo":"bar"}
json.Unmarshal
json.Unmarshal was the tough one to work out. As it turns out, all you need to do is define a map of string => interface and then unmarshal into this structure. Go will take care of the rest.
# Using the "str" variable that we created using json.Marshal earlier
var x map[string]interface{}
json.Unmarshal([]byte(str), &x)
fmt.Println("%v", x)
# => %v map[baz:map[bee:boo] foo:bar]
# Using some hand crafted JSON. This could come from a file, web service, anything
str2 := "{"foo":{"baz": [1,2,3]}}"
var y map[string]interface{}
json.Unmarshal([]byte(str2), &y)
fmt.Println("%v", y)
# => %v map[foo:map[baz:[1 2 3]]]
Once the data is un-marshalled, you can access it as follows:
fmt.Printf("%v", y["foo"].(map[string]interface{})["baz"])
# => [1,2,3]
As we’re un-marshalling into an interface, we need to inform go what data type each key is before we can perform operations on it. This is what the .(map[string]interface{})
does