为什么json.Unmarshal返回的是一个map而不是预期的结构体?

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

Why does json.Unmarshal return a map instead of the expected struct?

问题

请看这个示例:http://play.golang.org/p/dWku6SPqj5

基本上,我正在开发的库接收一个interface{}作为参数,然后需要将其从字节数组解析为json.Unmarshal。在底层,interface{}参数是一个与字节数组的json结构匹配的结构体,但是该库没有对该结构体的引用(但是通过reflect.Type有对应的引用)。

为什么json包无法检测到底层类型?由于某种原因,它返回的是一个简单的映射,而不是实际的结构体。

以下是代码:

package main

import "fmt"
import "encoding/json"
import "reflect"

func main() {
    good()
    bad()
}

func good() {
    var ping Ping = Ping{}
    deserialize([]byte(`{"id":42}`), &ping)
    fmt.Println("DONE:", ping.ID)
}

func bad() {
    var ping interface{} = Ping{}
    deserialize([]byte(`{"id":42}`), &ping)
    fmt.Println("DONE:", ping) // 现在它是一个简单的映射,而不是Ping。为什么?
}

func deserialize(stuff []byte, thing interface{}) {
    value := reflect.ValueOf(thing)
    fmt.Printf("%+v | %v\n", value, value.Kind())
        
    err := json.Unmarshal(stuff, thing)
    if err != nil {
        panic(err)
    }
}

type Ping struct {
    ID int `json:"id"`
}
英文:

See this playground: http://play.golang.org/p/dWku6SPqj5

Basically, the library I'm working on receives an interface{} as a parameter and then needs to json.Unmarshal that from a byte array. Under the covers, the interface{} parameter is a struct that matches the json structure of the byte array but the library doesn't have a reference to that struct (but it does have a reference to the corresponding reflect.Type through).

Why can't the json package detect the underlying type? For some reason it's giving back a simple map instead of the actual struct.

Here's the code:

package main

import "fmt"
import "encoding/json"
import "reflect"

func main() {
	good()
	bad()
}

func good() {
	var ping Ping = Ping{}
	deserialize([]byte(`{"id":42}`), &ping)
	fmt.Println("DONE:", ping.ID)
}

func bad() {
	var ping interface{} = Ping{}
	deserialize([]byte(`{"id":42}`), &ping)
	fmt.Println("DONE:", ping) // It's a simple map now, not a Ping. Why?
}

func deserialize(stuff []byte, thing interface{}) {
	value := reflect.ValueOf(thing)
	fmt.Printf("%+v | %v\n", value, value.Kind())
		
	err := json.Unmarshal(stuff, thing)
	if err != nil {
		panic(err)
	}
}

type Ping struct {
	ID int `json:"id"`
}

答案1

得分: 4

你已经将一个指向抽象接口的指针传递给了json。你应该简单地将一个指向Ping的指针作为抽象接口传递进去:

func bad() {
    var ping interface{} = &Ping{} // <<< 这里
    deserialize([]byte(`{"id":42}`), ping) // <<< 还有这里
    fmt.Println("DONE:", ping) // 现在它只是一个简单的映射,不再是Ping。为什么?
}

但是,如果你确实没有一个指针可以转换为interface{},你可以使用反射来创建一个新的指针,将其反序列化到其中,并将值复制回去:

func bad() {
    var ping interface{} = Ping{}
    nptr := reflect.New(reflect.TypeOf(ping))
    deserialize([]byte(`{"id":42}`), nptr.Interface())
    ping = nptr.Interface()
    fmt.Println("DONE:", ping) // 现在它只是一个简单的映射,不再是Ping。为什么?
}
英文:

You've passed to json a pointer to an abstract interface. You should simply pass a pointer to Ping as an abstract interface:

func bad() {
    var ping interface{} = &amp;Ping{} // &lt;&lt;&lt;&lt; this
    deserialize([]byte(`{&quot;id&quot;:42}`), ping) // &lt;&lt; and this
    fmt.Println(&quot;DONE:&quot;, ping) // It&#39;s a simple map now, not a Ping. Why?
}

But if as you said you don't have a pointer to cast ton an interface{}, you can use reflect to create a new pointer, deserialize into it, and copy the value back:

func bad() {
	var ping interface{} = Ping{}
	nptr := reflect.New(reflect.TypeOf(ping))
	deserialize([]byte(`{&quot;id&quot;:42}`), nptr.Interface())
	ping = nptr.Interface()
	fmt.Println(&quot;DONE:&quot;, ping) // It&#39;s a simple map now, not a Ping. Why?
}

huangapple
  • 本文由 发表于 2014年1月31日 06:25:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/21468741.html
匿名

发表评论

匿名网友

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

确定