What's the golang equivalent of converting any JSON to standard dict in Python?

huangapple go评论80阅读模式
英文:

What's the golang equivalent of converting any JSON to standard dict in Python?

问题

在Python中,你可以这样做:

import requests

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

现在,data是一个字典或哈希表(而且,我不需要事先定义字典的结构),我可以通过data["entities"]data["entities"]["Q12"]等方式访问键的值。

在Go语言中,你可以这样做:

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

func main() {
	resp, err := http.Get("http://wikidata.org/w/api.php?" + v.Encode())
	if err != nil {
		// 处理错误
	}

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

这样做会给你一个编译错误:invalid operation: data["entities"] (index of type interface {})

所以,var data应该是map[string]interface{}类型。你需要定义一个结构来处理JSON文件/流,还是可以在不修改代码的情况下处理任何JSON文件/流呢?

英文:

In Python you can do something like this:

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

And now data is a dict or hash table (also, I did not need to define beforehand the structure of the dict), and I can access values of keys by doing data["entities"], data["entities"]["Q12"], etc.

How would I do this in golang? So far I have this:

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])

Which gives me the compile error: invalid operation: data["entities"] (index of type interface {})

So what type should var data be? And do I need to define a structure to the JSON beforehand or is it possible to handle any JSON file/stream without modifying the code?

答案1

得分: 33

如果你想要一个字典,可以使用Go语言中的map[string]interface{}类型(它是一个具有string键和任意类型值的map):

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{})
// 如果 ok 为 true,innermap 的类型是 map[string]interface{}
// 你可以引用它的元素。
英文:

If you want a dictionary, use the Go type map[string]interface{} (which is a map with string keys and values of any type):

var data map[string]interface{}

And then you can refer to its elements like:

data["entities"]

See this example:

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)

Output:

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]]

Try it on the Go Playground.

Notes:

Basically if your map is multi-level (the map contains another map) like the "innermap" in the above example, when you access the inner map, you can use Type assertion to have it as another map:

innermap, ok := data["innermap"].(map[string]interface{})
// If ok, innermap is of type map[string]interface{}
// and you can refer to its elements.

答案2

得分: 3

我更喜欢添加类型声明,这样你就可以添加方法来简化类型断言:

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

英文:

I prefer to add a type declaration, that way you can add methods to simplify the
type assertions:

package main

type mapType map[string]interface{}

func (t mapType) m(s string) mapType {
   return t
展开收缩
.(map[string]interface{}) } func (t mapType) f(s string) float64 { return t
展开收缩
.(float64) }

Example:

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>

huangapple
  • 本文由 发表于 2015年3月5日 00:39:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/28859941.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定