问题
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