How to parse complex JSON using Unmarshal?

In go standard encoding / json package provides the json.Unmarshal function for parsing JSON.

You can either untie the JSON string in a predefined struct , or use interface{} and iterate the result for an unexpected JSON data structure.

However, I cannot parse complex JSON correctly. Can someone tell me how to do this?

  { "k1" : "v1", "k2" : "v2", "k3" : 10, "result" : [ [ ["v4", v5, {"k11" : "v11", "k22" : "v22"}] , ... , ["v4", v5, {"k33" : "v33", "k44" : "v44"} ] ], "v3" ] } 
+6
source share
3 answers

Referring to JSON and Go :

Without knowing this data structure, we can decode it into an interface {} value using 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 will be a mapping whose keys are strings and whose values ​​themselves are 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 statement to access the f base interface map [string] {}:

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

Then we can iterate over the map using the range operator and use the type switch to access its values ​​as specific 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") } } 

This way, you can work with unknown JSON data, while preserving the benefits of type safety.

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

+23
source

More recently, gjson offers a selection of properties in JSON

 k1 := gjson.Get(json, "k1") k33 : = gjson.Get(json, "result.#.#.k33") 
+2
source

Using the standard encoding/json package library

I worked on JSON and Go , and it turned out that case int not working, and it should be case float64 now, and in the real world of JSON there are many sockets.

 > go version go version go1.7.1 darwin/amd64 

I also looked at JSON decoding in Go , but it didn’t help me much, since I needed to go through a procedurally oriented series of calls to mruby binding, and the author of this article was satisfied with Go structures for the most part.

I spent a little time playing with this, and the last iteration function of the dumper looked like this:

 func dumpJSON(v interface{}, kn string) { iterMap := func(x map[string]interface{}, root string) { var knf string if root == "root" { knf = "%q:%q" } else { knf = "%s:%q" } for k, v := range x { dumpJSON(v, fmt.Sprintf(knf, root, k)) } } iterSlice := func(x []interface{}, root string) { var knf string if root == "root" { knf = "%q:[%d]" } else { knf = "%s:[%d]" } for k, v := range x { dumpJSON(v, fmt.Sprintf(knf, root, k)) } } switch vv := v.(type) { case string: fmt.Printf("%s => (string) %q\n", kn, vv) case bool: fmt.Printf("%s => (bool) %v\n", kn, vv) case float64: fmt.Printf("%s => (float64) %f\n", kn, vv) case map[string]interface{}: fmt.Printf("%s => (map[string]interface{}) ...\n", kn) iterMap(vv, kn) case []interface{}: fmt.Printf("%s => ([]interface{}) ...\n", kn) iterSlice(vv, kn) default: fmt.Printf("%s => (unknown?) ...\n", kn) } } 

If b is a byte slice with JSON representing either an array or an object at the top level, you can call it like this:

 var f interface{} if err := json.Unmarshal(b, &f); err != nil { panic(err) } dumpJSON(f, "root") 

Hope this helps, you will try the completion program here .

Using other packages

I would recommend not doing it yourself if you don’t feel that you need to learn how Go types work and using reflect , making you feel like a master of the universe (personally, reflect makes me furious).

Since @changingrainbows is listed below , there is github.com/tidwall/gjson which seems to complete encoding/json and use reflect . Perhaps I am no different from github.com/mitchellh/reflectwalk , which is quite difficult to use, and the internal workings are quite complicated.

I used github.com/buger/jsonparser quite extensively in one of my projects, as well as github.com/json-iterator/go , which I have not tried yet, but it seems to be based on github.com/buger/jsonparser and seems to expose an enconding/json compatible interface and has func Get(data []byte, path ...interface{}) Any . For the record, the Kubernetes project recently switched to github.com/json-iterator/go . In my project I use encoding/json as well as github.com/buger/jsonparser , I will probably switch to github.com/json-iterator/go when I have time. I will try to update this post with more results.

+1
source

Source: https://habr.com/ru/post/987553/


All Articles