如何告诉json.Unmarshal使用struct而不是interface [英] How to tell json.Unmarshal to use struct instead of interface

查看:136
本文介绍了如何告诉json.Unmarshal使用struct而不是interface的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想编写一个函数,该函数接收几种类型的结构并将其从JSON解组.为此,我有另一组带有预定义签名的函数,这些函数返回struct实例,但是由于每个函数都返回不同类型的struct,因此函数签名的返回类型为interface{}.

I want to write a function that receives several types of structs and unmarshals them from JSON. To this end, I have another set of functions with a pre-defined signature that return the struct instances but since each function returns a different type of struct the function signature has interface{} as the return type.

当我发送json.Unmarshal一个具体的结构时,它可以按我的预期工作,但是当我发送与interface{}相同的结构时,它将转换为地图.

When I send json.Unmarshal a concrete struct it works as I expected but when I send the same struct as interface{} it converts it to a map.

这是描述问题的简化示例代码:

Here is a simplified example code that depicts the problem:

package main

import (
"encoding/json"
    "fmt"
)

type Foo struct {
    Bar string `json:"bar"`
}  

func getFoo() interface{} {
    return Foo{"bar"}
}

func main() {

    fooInterface := getFoo() 
    fooStruct := Foo{"bar"}
    fmt.Println(fooInterface) //{bar}
    fmt.Println(fooStruct)    //{bar}

    myJSON := `{"bar":"This is the new value of bar"}`
    jsonBytes := []byte(myJSON)

    err := json.Unmarshal(jsonBytes, &fooInterface )
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(fooInterface) //map[bar:This is the new value of bar]

    err = json.Unmarshal(jsonBytes, &fooStruct)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(fooStruct) //{This is the new value of bar}
}

https://play.golang.org/p/tOO7Ki_i4c

我希望json.Unmarshal使用接口背后的具体结构进行编组,但实际上并没有,只是将值的映射分配给所传递的接口.

I expected json.Unmarshal to use the concrete struct behind the interface for unmarshaling but it doesn't and just assigns the map of values to the passed interface.

为什么它不使用具体结构,有没有办法告诉它使用具体结构类型而不进行显式转换(我在设计时不知道显式类型)?

Why doesn't it use the concrete struct and is there a way to tell it to use the concrete struct type without explicit casting (I don't know the explicit type at design time)?

推荐答案

encoding/json 程序包无法神奇地猜测出您想要将结果编组为哪种类型,除非您告知它.

The encoding/json package can't magically guess what type you want the result unmarshaled into, unless you tell it to.

告诉要取消存储的一种方法是将该类型的值传递给json.Unmarshal()函数.

One way of telling what to unmarsal into is to pass value of that type to the json.Unmarshal() function.

不幸的是,没有其他方法.如果您传递interface{}类型的值,则json包实现可以自由选择其选择的类型,它将为JSON对象选择map[string]interface{},为JSON数组选择[]interface{}.记录在 json.Unmarshal() :

And unfortunately there is no other way. If you pass a value of interface{} type, the json package implementation is free to choose a type of its choice, and it will choose map[string]interface{} for JSON objects, and []interface{} for JSON arrays. This is documented at json.Unmarshal():

要将JSON编组为接口值,Unmarshal将其中之一存储在接口值中:

To unmarshal JSON into an interface value, Unmarshal stores one of these in the interface value:

bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null

如果您事先知道类型,请创建该类型的值,然后将其传递以进行编组.事先是否将其存储在interface{}变量中无关紧要;如果传递的值适合解组,则将使用它.请注意,传递的值(如果不是该类型的话)将被包装在interface{}中,因为这是json.Unmarshal()的参数类型.

If you know the type beforehand, create a value of that type, and pass that for unmarshaling. Whether your store this in an interface{} variable beforehand does not matter; if the passed value is suitable for unmarshaling, it will be used. Note that the passed value will be wrapped in an interface{} if not already of that type, as that is the parameter type of json.Unmarshal().

代码失败的原因是因为您传递了一个*interface{}类型的值,该值包装了一个非指针Foo值.由于json软件包无法使用此软件包,因此会创建一个新的值(地图).

The problem why your code fails is because you pass a value of type *interface{} which wraps a non-pointer Foo value. Since the json package can't use this, it creates a new value of its choice (a map).

相反,您应该将*Foo值包装在interface{}中,然后传递该值:

Instead you should wrap a *Foo value in an interface{}, and pass that:

func getFoo() interface{} {
    return &Foo{"bar"}
}

func main() {
    fooInterface := getFoo()

    myJSON := `{"bar":"This is the new value of bar"}`
    jsonBytes := []byte(myJSON)

    err := json.Unmarshal(jsonBytes, fooInterface)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf("%T %+v", fooInterface, fooInterface)
}

这导致(在游乐场上尝试):

*main.Foo &{Bar:This is the new value of bar}

这篇关于如何告诉json.Unmarshal使用struct而不是interface的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆