我正在做非常基本的JSON操作来学习Go语言,并且可以正常工作,除非似乎有一件事情,我必须编写JSON的分配.(map[string]interface{})并.([]interface{})访问JSON中的条目,尤其是当它们是孩子的孩子时,等等。
.(map[string]interface{})
.([]interface{})
参见此处(也在Go Playground上:https : //play.golang.org/p/Wd-pzHqTsU):
package main import ( "fmt" "encoding/json" ) func main() { JSON := []byte(`{"key1":"val1","key2":{"c1key1":"c1val1"},"key3":[{"c2key1":{"c3key1":"c3val1"}}]}`) fmt.Printf("%s\n", JSON) var d map[string]interface{} json.Unmarshal(JSON, &d) fmt.Println(d["key3"].([]interface{})[0].(map[string]interface{})["c2key1"].(map[string]interface{})["c3key1"]) d["key3"].([]interface{})[0].(map[string]interface{})["c2key1"].(map[string]interface{})["c3key1"] = "change1" fmt.Println(d["key3"].([]interface{})[0].(map[string]interface{})["c2key1"].(map[string]interface{})["c3key1"]) JSON, _ = json.Marshal(d) fmt.Printf("%s\n", JSON) }
哪个返回:
{"key1":"val1","key2":{"c1key1":"c1val1"},"key3":[{"c2key1":{"c3key1":"c3val1"}}]} c3val1 change1 {"key1":"val1","key2":{"c1key1":"c1val1"},"key3":[{"c2key1":{"c3key1":"change1"}}]}
现在,在Python中,我直接访问键/值,而不是定义每次访问的类型,而不是fmt.Println(d["key3"].([]interface{})[0].(map[string]interface{})["c2key1"].(map[string]interface{})["c3key1"])您print d["key3"][0]["c2key1"]["c3key1"]
fmt.Println(d["key3"].([]interface{})[0].(map[string]interface{})["c2key1"].(map[string]interface{})["c3key1"])
print d["key3"][0]["c2key1"]["c3key1"]
Python示例:
import json JSON = '{"key3": [{"c2key1": {"c3key1": "c3val1"}}], "key2": {"c1key1": "c1val1"}, "key1": "val1"}' print JSON d = json.loads(JSON) print d["key3"][0]["c2key1"]["c3key1"] d["key3"][0]["c2key1"]["c3key1"] = "change1" print d["key3"][0]["c2key1"]["c3key1"] JSON = json.dumps(d) print JSON
那么我在Go中这样做正确吗?如果是这样,此设计的原因是什么?否则,我该怎么办?
前言: 我优化和改进了以下解决方案,并将其作为库发布在这里:github.com/icza/dyno。
github.com/icza/dyno
最干净的方法是创建struct对JSON建模的预定义类型(结构),并解组为该类型的值,并且您可以使用Selectors(用于struct类型)和Index表达式(用于地图和切片)简单地引用元素。
struct
但是,如果您的输入不是预定义的结构,则建议您使用以下2个辅助函数:get()和set()。第一个访问(返回)由任意路径(string映射键和/或int切片索引的列表)指定的任意元素,第二个访问(返回)由任意路径指定的值(这些帮助函数的实现在最后)答案)。
get()
set()
string
int
您只需在项目/应用程序中包含这两个功能。
现在,使用这些帮助器,您想要执行的任务变得非常简单(就像python解决方案一样):
fmt.Println(get(d, "key3", 0, "c2key1", "c3key1")) set("NEWVALUE", d, "key3", 0, "c2key1", "c3key1") fmt.Println(get(d, "key3", 0, "c2key1", "c3key1"))
输出:
change1 NEWVALUE
在Go Playground上尝试修改后的应用程序。
注意-进一步简化:
您甚至可以将路径保存在变量中,然后重复使用以进一步简化上面的代码:
path := []interface{}{"key3", 0, "c2key1", "c3key1"} fmt.Println(get(d, path...)) set("NEWVALUE", d, path...) fmt.Println(get(d, path...))
和的实现get(),并set()在下面。注意:检查路径是否有效被省略。此实现使用类型开关:
func get(m interface{}, path ...interface{}) interface{} { for _, p := range path { switch idx := p.(type) { case string: m = m.(map[string]interface{})[idx] case int: m = m.([]interface{})[idx] } } return m } func set(v interface{}, m interface{}, path ...interface{}) { for i, p := range path { last := i == len(path)-1 switch idx := p.(type) { case string: if last { m.(map[string]interface{})[idx] = v } else { m = m.(map[string]interface{})[idx] } case int: if last { m.([]interface{})[idx] = v } else { m = m.([]interface{})[idx] } } } }