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

时间:2015-05-20 06:11:21

标签: json parsing go

go中,标准包encoding/json公开了json.Unmarshal函数来解析JSON。

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

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

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

3 个答案:

答案 0 :(得分:34)

引自JSON and Go

  

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

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

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

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

要访问此数据,我们可以使用类型断言来访问f底层地图[string] interface {}:

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

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

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")
    }
}
  

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

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

答案 1 :(得分:4)

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

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

答案 2 :(得分:3)

使用标准库encoding/json

我曾经使用JSON and Go文章,结果发现case int不起作用,现在需要case float64,并且实际上有很多嵌套世界JSON。

> go version
go version go1.7.1 darwin/amd64

我也看过JSON decoding in Go,但它对我没有多大帮助,因为我需要将其程序化地转换为一系列对mruby绑定的调用,并且该文章的作者很满意大部分都是结构。

我花了一点时间摆弄这个,最后的迭代翻斗功能看起来像这样:

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)
    }
}

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

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

Hope this helps, you try the comple program here

使用其他包

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

作为@changingrainbows pointed out below,有github.com/tidwall/gjson个包,它似乎包裹encoding/json并使用reflect。我可能与github.com/mitchellh/reflectwalk没有什么不同,github.com/buger/jsonparser很难使用,而且内部工作非常复杂。

我在我的一个项目中使用了github.com/buger/jsonparser,还有github.com/json-iterator/go,我还没有尝试过,但它似乎是基于enconding/json和似乎公开了github.com/json-iterator/go兼容的界面,并且还有func Get(data []byte, path ...interface{}) Any。为了记录,Kubernetes项目最近已切换到encoding/json。 在我的项目中,我使用github.com/buger/jsonparser以及github.com/json-iterator/go,当我有时间时,我可能会切换到$budgetService = $user->GetService('BudgetService', ADWORDS_VERSION); $bm = new Money('10000000'); $campaign->budget->amount = $bm; $boperation = new BudgetOperation(); $boperation->operand = $campaign->budget; $boperation->operator = 'SET'; $budgetService->mutate($boperation); 。我会尝试用更多的发现来更新这篇文章。