使用JSON字符串,将其解组为map [string] interface {},编辑并将其编组为[]字节似乎更复杂然后它应该是

时间:2015-03-05 12:05:54

标签: json dictionary go

我正在做一些非常基本的JSON操作来学习一些Go,并且它有效,除了一件事似乎关闭,我必须编写分配.(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中这样做了吗?如果是这样,这个设计的原因是什么?或者如果没有,我该怎么做?

3 个答案:

答案 0 :(得分:12)

前言:我优化并改进了以下解决方案,并在此处将其作为库发布:github.com/icza/dyno


最干净的方法是创建为JSON建模的预定义类型(结构struct),并解组为该类型的值,并且您可以使用Selectors简单地引用元素(for { {1}}类型)和Index expressions(适用于地图和切片)。

但是,如果您的输入不是预定义的结构,我建议您使用以下2个辅助函数:structget()。第一个访问(返回)由任意路径(set()映射键和/或string切片索引的列表)指定的任意元素,第二个更改(设置)由任意路径指定的值(这些辅助函数的实现在答案的最后)。

您只需在项目/应用中包含这两个功能。

现在使用这些助手,你想要做的任务变得如此简单(就像python解决方案一样):

int

输出:

fmt.Println(get(d, "key3", 0, "c2key1", "c3key1"))
set("NEWVALUE", d, "key3", 0, "c2key1", "c3key1")
fmt.Println(get(d, "key3", 0, "c2key1", "c3key1"))

Go Playground上尝试修改后的应用。

注意 - 进一步简化:

您甚至可以将路径保存在变量中并重复使用它以进一步简化上述代码:

change1
NEWVALUE

path := []interface{}{"key3", 0, "c2key1", "c3key1"} fmt.Println(get(d, path...)) set("NEWVALUE", d, path...) fmt.Println(get(d, path...)) get()的实现如下。注意:省略检查路径是否有效。此实现使用Type switches

set()

答案 1 :(得分:3)

不,这不是在Go中处理结构化JSON数据的最正确方法。相反,最好创建一个" struct hierarchy"并将您的JSON解组为结构。 E.g。

type Data struct {
    Key1 string
    Key2 struct {
        C1Key1 string
    }
    Key3 []Key3
}

type Key3 struct {
    C2Key1 struct {
        C3Key1 string
    }
}

这种方法:

  • 让您更好地控制数据将如何(未)编组(通过结构标记和json.Unmarshalerjson.Marshaler接口)
  • 让你摆脱类型断言
  • 相反,为您提供更多类型安全
  • 具有更好的性能,因为与地图访问相比,struct access基本上是免费的。

游乐场:https://play.golang.org/p/9XIh8DX1Ms

答案 2 :(得分:0)

这是一个完整的示例,使用 struct 并进行编辑:

package main

import (
   "encoding/json"
   "os"
)

const s = `
{
   "key1": "val1",
   "key2": {"c1key1": "c1val1"},
   "key3": [{"c2key1": {"c3key1": "c3val1"}}]
}
`

func main() {
   var d struct {
      Key1 string
      Key2 struct { C1Key1 string }
      Key3 []struct {
         C2Key1 struct { C3Key1 string }
      }
   }
   json.Unmarshal([]byte(s), &d)
   d.Key3[0].C2Key1.C3Key1 = "change1"
   b, e := json.Marshal(d)
   if e != nil {
      panic(e)
   }
   os.Stdout.Write(b)
}