英文:
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{} = &Ping{} // <<<< this
deserialize([]byte(`{"id":42}`), ping) // << and this
fmt.Println("DONE:", ping) // It'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(`{"id":42}`), nptr.Interface())
ping = nptr.Interface()
fmt.Println("DONE:", ping) // It's a simple map now, not a Ping. Why?
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论