英文:
Polymorphic JSON unmarshalling of embedded structs
问题
这是一个示例(请参见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或string?或者是否可以解组到A.Y,因为B.Y已经定义了?
我知道有些人可能建议使用类似json.Unmarshal(data, &b.A)
的方式进行解组,但我不知道是否可以将其适应到我当前的设计中。
英文:
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
得分: 4
Go语言的唯一多态性是接口。嵌入不提供多态性。
如果你试图解析JSON,其中一个字段的类型是不确定的,你可以使用类型为interface{}
的字段,结合类型断言、fmt.Sprint
或反射来处理。具体使用哪种方法取决于特定的用例 - 一旦你获取到值,你打算如何处理它?在某个时刻,你必须关心它是int
还是string
,这将决定你如何处理该值。
英文:
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
得分: 2
根据Adrian的指示,Go语言不支持通过结构体嵌入实现多态性。interface{}
是在golang中持有任何类型变量的唯一方式。然而,在你的情况下,你可以实现自定义的Unmarshaler
来将JSON流解码为结构体,利用json.Number
或interface{}
。下面是使用json.Number
的实现方式。对于更通用的interface{}
版本,你可以按照Adrian的建议进行实现。
func (b *B) UnmarshalJSON(js []byte) error {
//首先:将流解码为匿名结构体
v := struct {
X string
Y json.Number
}{}
err := json.Unmarshal(js, &v)
if err != nil {
return err
}
//根据v.Y的值,选择持有变量
//如果可以转换为数字,赋值给A.Y
//否则,赋值给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中找到一个可工作的示例。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论