【问题标题】:Parsing JSON in go在 go 中解析 JSON
【发布时间】:2016-10-03 22:44:52
【问题描述】:

我的问题是关于在 golang 中解析 JSON 文件。

具体来说,我正在尝试解析命令“docker network inspect bridge”的输出,该命令恰好是 JSON 格式。该命令在此处进行了描述。我的目标是获取列出的容器的“IPv4Address”列表。

我尝试过这样做,但未能将 map[string]interface{} 转换为 map[string]string。我的代码在这里:- https://play.golang.org/p/eO_j996gGb

$ sudo docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "b2b1a2cba717161d984383fd68218cf70bbbd17d328496885f7c921333228b0f",
        "Scope": "local",
        "Driver": "bridge",
        "IPAM": {
            "Driver": "default",
            "Config": [
                {
                    "Subnet": "172.17.42.1/16",
                    "Gateway": "172.17.42.1"
                }
            ]
        },
        "Internal": false,
        "Containers": {
            "bda12f8922785d1f160be70736f26c1e331ab8aaf8ed8d56728508f2e2fd4727": {
                "Name": "container2",
                "EndpointID": "0aebb8fcd2b282abe1365979536f21ee4ceaf3ed56177c628eae9f706e00e019",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            },
            "f2870c98fd504370fb86e59f32cd0753b1ac9b69b7d80566ffc7192a82b3ed27": {
                "Name": "container1",
                "EndpointID": "a00676d9c91a96bbe5bcfb34f705387a33d7cc365bac1a29e4e9728df92d10ad",
                "MacAddress": "02:42:ac:11:00:01",
                "IPv4Address": "172.17.0.1/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        }
    }
]

在 golang 中解析此类 JSON 文件的正确方法是什么。我确实尝试过使用类型转换,但没有帮助。我尝试了很多东西,但最终只能达到 sn-p 中显示的代码。如何从“cont”对象中提取 IPv4Address 字段?

我拥有的去游乐场 sn-p 的链接是 https://play.golang.org/p/eO_j996gGb。非常感谢任何帮助(愿意尝试一下,不仅需要代码,还需要想法 :) 谢谢。

【问题讨论】:

  • 更多仅供参考:大多数 docker 命令接受 --format 选项,该选项需要是 Golang 模板(来自 text/template)包。

标签: json go


【解决方案1】:

只需修改您的代码以进一步解包数据。您将每个容器对象都保留为interface{},这样您就无法访问其中的字段。另一个断言使其成为map[string]interface{} 将允许您按名称访问其中的字段(如IPv4Address)。这是一个工作示例; https://play.golang.org/p/4OC5axN4Gd

这是重要的代码更改;

    containers := (foo[0]["Containers"]).(map[string]interface{})

    //fmt.Printf("containers %+v", containers)
    for _, v := range containers {
        unwrapped := v.(map[string]interface{})
        fmt.Printf("\ncont %+v\n", unwrapped["IPv4Address"])            
    }

v 只是一个interface{},因此如果不再次断言/转换为类型,您将无法访问IPv4Address 字段。

现在效果很好,但是,我建议不要在代码中大量使用接口。你的代码充满了不安全的操作,需要大量的错误处理(比如每次你尝试访问你有可能抛出的地图,以及任何时候你做一个类型断言)。这里有很多关于如何在 SO 上执行此操作的示例,但是如果您发表评论请求它,我可以生成另一个将解组为结构的示例。这在 imo 中更加安全和可维护,因为大多数错误都会在调用 unmarshal 时引发,而不是有大量代码可能会处理所有可能导致恐慌的结果。

这是一些包含所有类型并使用 unmarshal 的播放代码; https://play.golang.org/p/VfHst9GaiR

您的类型可以用以下方式表示;

type DockerInstance struct {
    Name string `json:"Name"`
    Id string `json:"Id"`
    Scope string `json:"Scope"`
    Driver string `json:"Driver"`   
    EnableIPv6 bool `json:"EnableIPv6"`
    IPAM IPAM `json:"IPAM"`
    Internal bool `json:"Internal"`
    Containers map[string]Container `json:"Containers"`
    Options map[string]string `json:"Options"`
    Labels interface{} `json:"Labels"`
}

type IPAM struct {
    Driver string `json:"Driver"`
    Options interface{} `json:"Options"`
    Config []Conf `json:"Config"`
}

type Conf struct {
    Subnet string `json:"Subnet"`
}

type Container struct {
    Name string `json:"Name"`
        EndPointID string `json:"EndpointID"`
    MacAddress string `json:"MacAddress"`
        IPv4Address string `json:"IPv4Address"`
    IPv6Address string `json:"IPv6Address"`
}

我仍然设置了一些接口,这只是因为示例 json 不包含它们的任何数据,所以我不知道它们在 Go 源中的类型应该是什么。这是我还在您的主目录中添加的一些基本内容,用于测试对象/确保我的内容定义正确;

docks := []DockerInstance{} //note you're using an array of the top level type
err := json.Unmarshal([]byte(teststring), &docks)
if err != nil {
    fmt.Println(err)
} else {
    fmt.Println()
    fmt.Println()
    fmt.Println(docks[0].Name)
}

for k, v := range docks[0].Containers {
    fmt.Println(k)
    fmt.Println(v.IPv4Address)
}

for k, v := range docks[0].Options {
      fmt.Println(k)
    fmt.Println(v)
}

【讨论】:

  • 非常感谢。昨天在尝试时,我非常接近您的解决方案中的代码,但有时事情不起作用:(。是的,我对您在回答中解释的更安全的方式感兴趣。您能否也为此提供JSON。再次感谢。
  • 实际上,我只需要一个代表 JSON 的等效结构并将其解组(如果这就是您的意思)。如果您提供一些指示,我可以自己尝试一下。谢谢。
  • @GauravSinha 是的,这就是我的意思,这是一个例子; play.golang.org/p/VfHst9GaiR 我将一些相关代码发布到我的答案中,尽管我做了很多草率的工作只是为了证明数据在那里/显示如何访问它。
  • @GauravSinha 是的。我还包括了 json 标记,但在这个特定示例中它们不是必需的,因为 Go 中的字段名称与 json 中的字段名称完全匹配。但是,您必须使用它们是很常见的(最明显的例子是当 json 中的字段小写时,您通常需要在 Go 中导出它们,这使得它们与 json 中的不同)。
  • @GauravSinha 我觉得两者基本是等价的,我只是用了unmarshal,因为习惯了,觉得更方便。我必须做一些研究才能确定这一点。
猜你喜欢
  • 2014-09-20
  • 1970-01-01
  • 2018-12-18
  • 1970-01-01
  • 2016-12-16
  • 2020-10-07
  • 2021-11-09
  • 1970-01-01
  • 2012-06-15
相关资源
最近更新 更多