嵌入式结构的多态JSON解组

时间:2017-06-06 01:34:40

标签: json go nested polymorphism unmarshalling

以下是一个示例(另请参阅https://play.golang.org/p/or7z4Xc8tN):

package main

import (
    "encoding/json"
    "fmt"
)

type A struct {
    X string
    Y int
}

type B struct {
    A
    Y string 
}

func main() {
    data := []byte(`{"X": "me", "Y": "hi"}`)
    b := &B{}
    json.Unmarshal(data, b)
    fmt.Println(b)
    fmt.Println(b.A)

    b = &B{}
    data = []byte(`{"X": "me", "Y": 123}`)
    json.Unmarshal(data, b)
    fmt.Println(b)
    fmt.Println(b.A)
}

哪个输出:

&{{me 0} hi}
{me 0}
&{{me 0} }
{me 0}

有没有办法将字段Y多态解组为int或字符串?甚至在B.Y被定义之后,甚至在A.Y中解组?

我知道有些人可能建议用json.Unmarshall(data, &b.A)之类的东西进行解组,但我不知道我是否可以将其纳入我目前的设计中。

2 个答案:

答案 0 :(得分:2)

Go的唯一多态是接口。嵌入不提供多态性。

如果您尝试解组JSON,而不能假设其中一个字段类型,则可以使用类型为interface{}的字段以及类型断言{ {1}},或反思。你应该使用哪种取决于具体的用例 - 一旦你获得了价值,你将用它做什么?在某些时候,您必须关心它是fmt.Sprint还是int,这将决定您如何处理该值。

答案 1 :(得分:1)

正如Adrian所指出的,Go不支持通过struct embedding进行多态性。 interface{}是保存任何类型的golang变量的唯一方法。但是,在您的情况下,您可以实现自定义Unmarshaler以使用json.Numberinterface{}将JSON流解码为结构。以下是使用json.Number的实现。对于更通用的interface{}版本,您可以按照Adrian的建议实现它。

func (b *B) UnmarshalJSON(js []byte) error {
    //First: decode stream to anonymous struct
    v := struct {
        X string
        Y json.Number
    }{}

    err := json.Unmarshal(js, &v)
    if err != nil {
        return err
    }

    //Depends on the v.Y value, choose the holder variable
    //If it's convertible to number, assign to A.Y
    //otherwise, assign it to b.Y
    b.X = v.X
    if fv, err := v.Y.Float64(); err == nil {
        b.A.Y = int(fv)
    } else {
        b.Y = v.Y.String()
    }

    return nil
}

可以在The Go Playground中找到工作示例。