如何使用Go unmarshal解析复杂的JSON?

wel*_*lls 10 parsing json go

go标准包中编码/ json公开json.Unmarshal函数来解析JSON.

可以在预定义的情况下解组JSON字符串struct,或者使用interface{}和迭代结果以获得意外的JSON数据结构.

也就是说,我无法正确解析复杂的JSON.有人能告诉我如何实现这一目标吗?

 {
     "k1" : "v1", 
     "k2" : "v2", 
     "k3" : 10, 
     "result" : [
                 [
                 ["v4", v5, {"k11" : "v11", "k22" : "v22"}]
                 , ... , 
                 ["v4", v5, {"k33" : "v33", "k44" : "v44"}
                 ]
                 ], 
                 "v3"
                ] 
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*röm 35

引用JSON和Go:

在不知道这些数据的结构的情况下,我们可以使用Unmarshal将其解码为interface {}值:

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

此时,f中的Go值将是一个映射,其键是字符串,其值本身存储为空接口值:

f = map[string]interface{}{
    "k1": "v1",
    "k3":  10,
    "result": []interface{}{
       "v4",
       12.3,
       map[string]interface{}{
           "k11":"v11",
           "k22":"v22",
       },
    },
}
Run Code Online (Sandbox Code Playgroud)

要访问此数据,我们可以使用类型断言来访问f底层映射[string]接口{}:

m := f.(map[string]interface{})
Run Code Online (Sandbox Code Playgroud)

然后我们可以使用范围语句迭代映射,并使用类型开关来访问其值作为其具体类型:

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")
    }
}
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以使用未知的JSON数据,同时仍然享受类型安全的好处.

有关Go和JSON的更多信息,请参阅原始文章.我稍微改变了代码片段,使其更像是问题中的JSON.


err*_*per 6

使用标准库encoding/json

我研究过JSON 和 Go文章,结果发现这case int行不通,但case float64现在就需要这样,而且现实世界的 JSON 中有大量嵌套。

> go version
go version go1.7.1 darwin/amd64
Run Code Online (Sandbox Code Playgroud)

我还研究了Go 中的 JSON 解码,但它对我没有多大帮助,因为我需要将其转换为一系列对 mruby 绑定的调用,并且该文章的作者对大多数情况下的 Go 结构感到满意部分。

我花了一些时间摆弄这个,最终的迭代转储器函数如下所示:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

作为b一个带有表示顶层数组或对象的 JSON 的字节切片,您可以这样调用它:

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

希望这有帮助,您可以尝试这里的完整程序

使用其他包

我建议不要自己这样做,除非你觉得你必须学习 Go 类型是如何工作的,并且使用reflect会让你感觉自己是宇宙的大师(就个人而言,reflect让我发疯)。

正如@changerainbows 在下面指出的那样,有一个github.com/tidwall/gjsonpackage,它似乎包装encoding/json并使用reflect。我可能与github.com/mitchellh/reflectwalk,它很难使用并且内部工作相当复杂。

我在我的一个项目中相当广泛地使用了github.com/buger/jsonparser,而且还有github.com/json-iterator/go,我还没有尝试过,但它似乎基于github.com/buger/jsonparser并似乎公开了enconding/json兼容的接口,并且func Get(data []byte, path ...interface{}) Any也有。根据记录,Kubernetes 项目最近已切换到github.com/json-iterator/go. 在我的项目中,我使用encoding/json以及,当我有时间时github.com/buger/jsonparser我可能会切换到。github.com/json-iterator/go我将尝试用更多的发现来更新这篇文章。


cha*_*ows 5

最近,gjson提供了 JSON 中的属性选择

k1 := gjson.Get(json, "k1")
k33 : = gjson.Get(json, "result.#.#.k33")
Run Code Online (Sandbox Code Playgroud)