我优化并改进了以下解决方案,并将其作为库发布在这里:github.com/icza/dyno
。
最干净的方法是创建struct
对JSON建模的预定义类型(结构),并解组为该类型的值,并且您可以使用Selectors(用于struct
类型)和Index表达式(用于地图和切片)简单地引用元素。
但是,如果您输入的内容不是预定义的结构,建议您使用以下2个辅助函数: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]
}
}
}
}