Polymorphic JSON unmarshalling of embedded structs

僤鯓⒐⒋嵵緔 提交于 2019-12-11 04:44:57

问题


Here is an example (see also 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)
}

Which outputs:

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

Is there a way to polymorphically unmarshal the field Y to either an int or a string? Or even unmarshal into A.Y at all since B.Y is defined?

I know some might suggest unmarshalling with something like json.Unmarshall(data, &b.A), but I don't know if I can fit that into my current design.


回答1:


Go's only polymorphism is interfaces. Embedding does not offer polymorphism.

If you're trying to unmarshal JSON where you can't assume what type one of the fields is going to be, you can use a field of type interface{} along with type assertions, fmt.Sprint, or reflection. Which you should use depends on the particular use case - once you've got the value, what are you going to do with it? At some point you have to care if it's an int or a string, which will determine how you handle the value.




回答2:


As pointed by Adrian, Go does not support polymorphism through struct embedding. interface{} is the only way to hold any type of golang variable. However, in your case you can implement custom Unmarshaler to decode the JSON stream to a struct utilizing json.Number or interface{}. Below is implementation using json.Number. For more generic interface{} version, you can implement it as suggested by 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
}

Working example can be found in The Go Playground.



来源:https://stackoverflow.com/questions/44380095/polymorphic-json-unmarshalling-of-embedded-structs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!