Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse a complicated JSON with Go unmarshal?

Tags:

json

parsing

go

In go the standard package encoding/json exposes json.Unmarshal function to parse JSON.

It's possible to either unmarshal the JSON string in a predefined struct, or use the interface{} and iterate the result for unexpected JSON data structure.

That said, I can't parse complex JSON properly. Can someone tell me how to achieve this?

 {
     "k1" : "v1", 
     "k2" : "v2", 
     "k3" : 10, 
     "result" : [
                 [
                 ["v4", v5, {"k11" : "v11", "k22" : "v22"}]
                 , ... , 
                 ["v4", v5, {"k33" : "v33", "k44" : "v44"}
                 ]
                 ], 
                 "v3"
                ] 
}
like image 440
wells Avatar asked May 20 '15 06:05

wells


People also ask

How do I read JSON data in Golang?

json is read with the ioutil. ReadFile() function, which returns a byte slice that is decoded into the struct instance using the json. Unmarshal() function. At last, the struct instance member values are printed using for loop to demonstrate that the JSON file was decoded.

How do I use Unmarshal JSON?

To parse JSON, we use the Unmarshal() function in package encoding/json to unpack or decode the data from JSON to a struct. Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. Note: If v is nil or not a pointer, Unmarshal returns an InvalidUnmarshalError.

How does JSON Unmarshal work Golang?

To unmarshal a JSON array into a slice, Unmarshal resets the slice length to zero and then appends each element to the slice. As a special case, to unmarshal an empty JSON array into a slice, Unmarshal replaces the slice with a new empty slice.


2 Answers

Citing from JSON and Go:

Without knowing this data's structure, we can decode it into an interface{} value with Unmarshal:

b := []byte(`{
   "k1" : "v1", 
   "k3" : 10,
   result:["v4",12.3,{"k11" : "v11", "k22" : "v22"}]
}`)
var f interface{}
err := json.Unmarshal(b, &f)

At this point the Go value in f would be a map whose keys are strings and whose values are themselves stored as empty interface values:

f = map[string]interface{}{
    "k1": "v1",
    "k3":  10,
    "result": []interface{}{
       "v4",
       12.3,
       map[string]interface{}{
           "k11":"v11",
           "k22":"v22",
       },
    },
}

To access this data we can use a type assertion to access f's underlying map[string]interface{}:

m := f.(map[string]interface{})

We can then iterate through the map with a range statement and use a type switch to access its values as their concrete types:

for k, v := range m {
    switch vv := v.(type) {
    case string:
        fmt.Println(k, "is string", vv)
    case int:
        fmt.Println(k, "is int", vv)
    case []interface{}:
        fmt.Println(k, "is an array:")
        for i, u := range vv {
            fmt.Println(i, u)
        }
    default:
        fmt.Println(k, "is of a type I don't know how to handle")
    }
}

In this way you can work with unknown JSON data while still enjoying the benefits of type safety.

More information about Go and JSON can be found in the original article. I changed the code snippets slightly to be more similar to the JSON in the question.

like image 195
Johan Wikström Avatar answered Sep 30 '22 09:09

Johan Wikström


More recently, gjson offers selection of properties in JSON

k1 := gjson.Get(json, "k1")
k33 : = gjson.Get(json, "result.#.#.k33")
like image 20
changingrainbows Avatar answered Sep 30 '22 09:09

changingrainbows