一尘不染

取一个JSON字符串,将其解组到map [string] interface {},进行编辑,然后将其编组为[] byte似乎要复杂得多,

go

我正在做非常基本的JSON操作来学习Go语言,并且可以正常工作,除非似乎有一件事情,我必须编写JSON的分配.(map[string]interface{}).([]interface{})访问JSON中的条目,尤其是当它们是孩子的孩子时,等等。

参见此处(也在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"]

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中这样做正确吗?如果是这样,此设计的原因是什么?否则,我该怎么办?


阅读 246

收藏
2020-07-02

共1个答案

一尘不染

前言:
我优化并改进了以下解决方案,并将其作为库发布在这里: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]
            }
        }
    }
}
2020-07-02