#json #go
#json #Вперед
Вопрос:
Мой вопрос касается синтаксического анализа файлов JSON в golang.
В частности, я пытаюсь проанализировать вывод команды «docker network inspect bridge», которая оказывается в формате JSON. Команда описана здесь . Моя цель — получить список «IPv4address» для перечисленных контейнеров.
Я пытался это сделать, но не смог преобразовать интерфейс map[string]{} в строку map[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"
}
}
]
Каков правильный способ анализа таких файлов JSON в golang. Я пробовал использовать преобразование типов, но это не помогает. Я перепробовал много вещей, но, наконец, смог добраться только до кода, показанного во фрагменте. Как я могу извлечь поле IPv4address из объекта «cont»?
Ссылка на фрагмент go playground у меня есть https://play.golang.org/p/eO_j996gGb . Любая помощь приветствуется (готов попробовать, нужен не только код, но и идеи 🙂 Спасибо.
Комментарии:
1. К вашему сведению: большинство команд docker принимают
--format
параметр, который должен быть шаблоном Golang (изtext/template
пакета).
Ответ №1:
Ваш код просто нужно изменить для дальнейшей распаковки данных. Вы оставляете его с каждым объектом контейнера, представленным как an interface{}
, что делает его таким, что вы не можете получить доступ к полям внутри него. Другое утверждение для каждого, чтобы сделать его a 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 % vn", 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), amp;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)
}
Комментарии:
1. Большое вам спасибо. Я очень близко подошел к коду, как в вашем решении, когда вчера пробовал что-то, но иногда что-то просто не работает :(. Да, меня интересует более безопасный способ, который вы объяснили в своем ответе. Не могли бы вы предоставить это и для этого JSON. Еще раз спасибо.
2. На самом деле, мне просто нужно иметь эквивалентную структуру, представляющую JSON и Unmarshal в нее (если это то, что вы имели в виду). Я могу попробовать это сам, если вы дадите несколько советов. Спасибо.
3. @GauravSinha да, это то, что я имею в виду, и вот пример этого; play.golang.org/p/VfHst9GaiR Я опубликую часть соответствующего кода в своем ответе, хотя я сделал много небрежных вещей, просто чтобы доказать, что данные есть / показать, как получить к ним доступ.
4. @GauravSinha да. Я также включил теги json, они не нужны в этом конкретном примере, потому что имена полей в Go точно соответствуют именам в json. Довольно часто вам приходится их использовать (наиболее очевидный пример — когда поля в json имеют нижний регистр, обычно их нужно экспортировать в Go, что отличает их от того, что есть в json).
5. @GauravSinha Я думаю, что эти два в основном эквивалентны, я просто использовал unmarshal, потому что я привык к этому, и я думаю, что это удобнее. Мне нужно было бы провести небольшое исследование, чтобы быть уверенным в этом.