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

One thought on “golang: Encode/Decode arbitrary JSON

  1. Thought you might be interested in adding use of reflection to work through more complex JSON where maps and lists contain arbitrary (JSON) types, Sample code below:

    
    package main
    
    import (
        "fmt"
        "encoding/json"
        "reflect"
    )
    
    func main() {
    //ref:  http://michaelheap.com/golang-encodedecode-arbitrary-json/  18/8/15
    
        // Using some hand crafted JSON. This could come from a file, web service, anything
        str2 := "{\"foo\":{\"baz\": [1,2,3]}, \"flag\":true, \"list\":[\"one\", 2, true, \"4\", {\"key\":\"value\"}, [1, true]]}"
    
        var y map[string]interface{}
        json.Unmarshal([]byte(str2), &y)
    
        fmt.Printf("%+v\n", y)
        //# => map[foo:map[baz:[1 2 3]] flag:true list:[one 2 true 4 map[key:value] [1 true]]]
    
    /*
    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. Go provides a the "reflect"
    package which we can use to process arbitrarily complex data structures:
    */
    
        the_list := y["list"].([]interface{})
        for n, v := range the_list {
            fmt.Printf("index:%d  value:%v  kind:%s  type:%s\n", n, v, reflect.TypeOf(v).Kind(), reflect.TypeOf(v))
        }
    
        //# =>
        //index:0  value:one  kind:string  type:string
        //index:1  value:2  kind:float64  type:float64
        //index:2  value:true  kind:bool  type:bool
        //index:3  value:4  kind:string  type:string
        //index:4  value:map[key:value]  kind:map  type:map[string]interface {}
        //index:5  value:[1 true]  kind:slice  type:[]interface {}
    }
    

Leave a Reply