我在寻找一种不涉及引入一个额外的“通用”字段的溶液等Value,Data等这将是该变种领域的占位符。
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类型应为哪种类型。
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,我可以这样做:
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字段,dynamic_field然后基于该字段,使DynamicFielda DynamicTypeA或DynamicTypeB。
UnmarshalJSON
Type
dynamic_field
DynamicField
DynamicTypeA
DynamicTypeB
但是如何?由于递归可能不起作用的一种方法是:
json:"-"
map[string]interface{}
…但是当我尝试将数据解组到a时将导致在第五步中无限递归,BigStruct后者将调用UnmarshalJSON当前正在执行的相同函数。
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)) }
https://play.golang.com/p/at5Okp3VU2u