golang: Encode/Decode arbitrary JSON

04 Aug 2014 in TIL

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:

go
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.

go
# 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:

go
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