我正在寻找不涉及引入其他“通用”字段(例如Value
,Data
等)的解决方案,该字段将用作变体字段的占位符。
我有一个JSON规范,它描述了几个大型结构,这些结构大多包含简单的值,但偶尔也有一个值本身就是结构,其动态类型取决于某个字段的值。
例如,这两个JSON文档都应解组为同一个Go结构:
{
"some_data": "foo",
"dynamic_field": { "type": "A", "name": "Johnny" },
"other_data": "bar"
}
和
{
"some_data": "foo",
"dynamic_field": { "type": "B", "address": "Somewhere" },
"other_data": "bar"
}
设置了JSON结构,我无法更改。
Go结构必须如下所示:
type BigStruct struct {
SomeData string `json:"some_data"`
DynamicField Something `json:"dynamic_field"`
OtherData string `json:"other_data"`
}
问题是如何实际执行以及该Something
类型应该是什么。
我首先将其设置为界面:
type Something interface {
GetType() string
}
并且有几个结构和函数可以配合使用:
type BaseDynamicType struct {
Type string `json:"type"`
}
type DynamicTypeA struct {
BaseDynamicType
Name string `json:"name"`
}
type DynamicTypeB struct {
BaseDynamicType
Address string `json:"address"`
}
func (d *BaseDynamicType) GetType() string {
return d.Type
}
原因是,当我获得BigStruct
的实例时,我可以这样做:
switch big.DynamicField.GetType() {
case "A": // do something with big.DynamicField cast to DynamicTypeA
case "B": // do something with big.DynamicField cast to DynamicTypeB
}
但是,然后我陷入了困境-这种安排如何与UnmarshalJSON
一起使用?我认为BigStruct
应该实现UnmarshalJSON
,它将以某种方式检查Type
的{{1}}字段,然后基于它,使dynamic_field
成为{{1 }}或DynamicField
。
但是如何?一种可能由于递归而不起作用的方法是:
DynamicTypeA
标记为DynamicTypeB
DynamicField
json:"-"
的{{1}}的{{1}}中,UnmarshalJSON
值,手动构造map[string]interface{}
或BigStruct
UnmarshalJSON
dynamic_field
...但是在第五步中,当我尝试将数据解组到DynamicTypeA
时将导致无限递归,该DynamicTypeB
会调用当前正在执行的同一BigStruct
函数。
答案 0 :(得分:1)
type BigStruct struct {
SomeData string `json:"some_data"`
DynamicField DynamicType `json:"dynamic_field"`
OtherData string `json:"other_data"`
}
type DynamicType struct {
Value interface{}
}
func (d *DynamicType) UnmarshalJSON(data []byte) error {
var typ struct {
Type string `json:"type"`
}
if err := json.Unmarshal(data, &typ); err != nil {
return err
}
switch typ.Type {
case "A":
d.Value = new(TypeA)
case "B":
d.Value = new(TypeB)
}
return json.Unmarshal(data, d.Value)
}
type TypeA struct {
Name string `json:"name"`
}
type TypeB struct {
Address string `json:"address"`
}
https://play.golang.com/p/oKMKQTdzp7s
如果您不想或不能更改DynamicField的类型,可以将UnmarshalJSON方法放在BigStruct上,并声明一个临时类型以避免递归。
func (b *BigStruct) UnmarshalJSON(data []byte) error {
var typ struct {
DF struct {
Type string `json:"type"`
} `json:"dynamic_field"`
}
if err := json.Unmarshal(data, &typ); err != nil {
return err
}
switch typ.DF.Type {
case "A":
b.DynamicField = new(DynamicTypeA)
case "B":
b.DynamicField = new(DynamicTypeB)
}
type tmp BigStruct // avoids infinite recursion
return json.Unmarshal(data, (*tmp)(b))
}