在 Go 中,当字段可以根据其他字段具有不同类型时,解组 JSON 的正确方法是什么?

问题描述 投票:0回答:1

假设我有一条如下所示的 JSON 消息:

{
    "type": string
    "data": list of something that is based on above type
}

可能有两个例子

{
    "type": "car"
    "data": [{"color": "red", "mpg": 16.4}]
}

{
    "type": "house"
    "data": [{"color": "blue", "state": "CA"}]
}

我想定义一个结构体,这样我基本上只能解码类型,然后使用它来正确地解组数据字段。我尝试了以下

type message struct {
    Type string `json:"type"`
    Data []byte `json:"data"`
}

type car struct {
    Color string
    MPG   float64
}

type house struct {
    Color string
    State string
}

错误地认为它只会让数据字段保持原始状态,以便我稍后将其解组到

car
house
结构中。我知道我可以将 Data 定义为
[]interface{}
并做一些其他工作来获得我想要的,但我想知道这是否是(目前)Go 中最好的方法?如果出现这种情况,假设我无法更改 JSON 定义 - 我只是这里服务的使用者。

json go
1个回答
1
投票

这是

json.RawMessage
的完美用例。查看那里的
Unmarshal
示例。

对于您的示例,这看起来像:

type message struct {
    Type string            `json:"type"`
    Data []json.RawMessage `json:"data"`
}

type car struct {
    Color string
    MPG   float64
}

type house struct {
    Color string
    State string
}

func main() {
    if err := parseAndPrint(carJSON); err != nil {
        panic(err)
    }
    if err := parseAndPrint(houseJSON); err != nil {
        panic(err)
    }
}

func parseAndPrint(b []byte) error {
    msg := new(message)
    if err := json.Unmarshal(b, msg); err != nil {
        panic(err)
    }

    switch msg.Type {
    case "car":
        for _, data := range msg.Data {
            c := new(car)
            if err := json.Unmarshal(data, c); err != nil {
                return err
            }
            fmt.Println(c)
        }
    case "house":
        for _, data := range msg.Data {
            h := new(house)
            if err := json.Unmarshal(data, h); err != nil {
                return err
            }
            fmt.Println(h)
        }
    }
    return nil
}


// Tucked here to get out of the way of the example
var carJSON = []byte(`
{
    "type": "car",
    "data": [{"color": "red", "mpg": 16.4}]
}
`)

var houseJSON = []byte(`{
    "type": "house",
    "data": [{"color": "blue", "state": "CA"}]
}
`)

现在您对解析结果的处理方式取决于您和您的程序的需求。在边缘解析并仅传递完整类型的消息,向外部结构体添加

Parsed any
字段等

© www.soinside.com 2019 - 2024. All rights reserved.