Что такое golang-эквивалент преобразования любого JSON в стандартный dict в Python?

В Python вы можете сделать что-то вроде этого:

r = requests.get("http://wikidata.org/w/api.php", params=params)
data = r.json()

И теперь data — это словарь или хеш-таблица (к тому же мне не нужно было заранее определять структуру словаря), и я могу получить доступ к значениям ключей, выполнив data["entities"], data["entities"][" Q12"] и т. д.

Как бы я сделал это в голанге? Пока у меня это:

resp, err := http.Get("http://wikidata.org/w/api.php?"+v.Encode())
if err != nil {
    // handle error
}

defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
var data interface{}
decodeErr := decoder.Decode(&data)
if decodeErr != nil {
    // handle error
}
fmt.Println(data["entities"], data["entities"]["Q"+id])

Что дает мне ошибку компиляции: invalid operation: data["entities"] (index of type interface {})

Итак, какого типа должен быть var data? И нужно ли заранее определять структуру JSON или можно обрабатывать любой файл/поток JSON без изменения кода?


person 01AutoMonkey    schedule 04.03.2015    source источник


Ответы (2)


Если вам нужен словарь, используйте тип Go map[string]interface{} (это map с ключами string и значениями любого типа):

var data map[string]interface{}

И тогда вы можете ссылаться на его элементы, например:

data["entities"]

См. этот пример:

s := `{"text":"I'm a text.","number":1234,"floats":[1.1,2.2,3.3],
    "innermap":{"foo":1,"bar":2}}`

var data map[string]interface{}
err := json.Unmarshal([]byte(s), &data)
if err != nil {
    panic(err)
}

fmt.Println("text =", data["text"])
fmt.Println("number =", data["number"])
fmt.Println("floats =", data["floats"])
fmt.Println("innermap =", data["innermap"])

innermap, ok := data["innermap"].(map[string]interface{})
if !ok {
    panic("inner map is not a map!")
}
fmt.Println("innermap.foo =", innermap["foo"])
fmt.Println("innermap.bar =", innermap["bar"])

fmt.Println("The whole map:", data)

Выход:

text = I'm a text.
number = 1234
floats = [1.1 2.2 3.3]
innermap = map[foo:1 bar:2]
innermap.foo = 1
innermap.bar = 2
The whole map: map[text:I'm a text. number:1234 floats:[1.1 2.2 3.3]
    innermap:map[foo:1 bar:2]]

Попробуйте его на Go Playground.

Примечания:

По сути, если ваша карта многоуровневая (map содержит еще один map), как "innermap" в приведенном выше примере, при доступе к внутренней карте вы можете использовать Введите утверждение, чтобы использовать его как другую карту:

innermap, ok := data["innermap"].(map[string]interface{})
// If ok, innermap is of type map[string]interface{}
// and you can refer to its elements.
person icza    schedule 04.03.2015
comment
Я хочу словарь, да, но он должен поддерживать несколько уровней иерархии или родитель-потомок, то есть он поддерживает только один уровень или {"key1": "val1", "key2": "val2"}, но мне также нужно поддерживать произвольное количество уровней, например {"key1": {"c1key1": "c1val1"}} или {"key1": {"c1key1": {"c2key1: "c2val1"}}}. - person 01AutoMonkey; 04.03.2015
comment
@01AutoMonkey Это поддерживает многоуровневые карты неограниченной глубины. Я отредактировал свой ответ, включив внутреннюю карту. - person icza; 04.03.2015

Я предпочитаю добавлять объявление типа, чтобы вы могли добавлять методы для упрощения утверждений типа:

package main

type mapType map[string]interface{}

func (t mapType) m(s string) mapType {
   return t[s].(map[string]interface{})
}

func (t mapType) f(s string) float64 {
   return t[s].(float64)
}

Пример:

package main

import (
   "encoding/json"
   "net/http"
)

func main() {
   req, err := http.NewRequest("GET", "http://wikidata.org/w/api.php", nil)
   if err != nil {
      panic(err)
   }
   q := req.URL.Query()
   q.Set("action", "wbgetentities")
   q.Set("format", "json")
   q.Set("ids", "Q24871")
   req.URL.RawQuery = q.Encode()
   res, err := new(http.Client).Do(req)
   if err != nil {
      panic(err)
   }
   defer res.Body.Close()
   var t mapType
   json.NewDecoder(res.Body).Decode(&t)
   println(t.m("entities").m("Q24871").f("pageid") == 28268)
}

https://golang.org/ref/spec#Type_declarations

person Steven Penny    schedule 31.12.2020
comment
Будьте осторожны, используя mapType методы, предложенные здесь. Методы паникуют, когда значение отсутствует или значение не имеет ожидаемого типа. - person thwd; 08.07.2021