在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.
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/gjson
package,它似乎包装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
我将尝试用更多的发现来更新这篇文章。
最近,gjson提供了 JSON 中的属性选择
k1 := gjson.Get(json, "k1")
k33 : = gjson.Get(json, "result.#.#.k33")
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
12430 次 |
最近记录: |