【发布时间】:2019-03-18 15:05:34
【问题描述】:
我正在寻找一种解决方案,它不涉及引入额外的“通用”字段,如 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,它会以某种方式检查dynamic_field 的Type 字段,然后基于它,使DynamicField 成为DynamicTypeA 或DynamicTypeB。
但是怎么做呢?由于递归而可能不起作用的一种方法是:
- 将
DynamicField标记为json:"-" - 为 BigStruct 实现
UnmarshalJSON - 在
BigStruct的UnmarshalJSON中将JSON解组为map[string]interface{}, - 检查地图中的
dynamic_field值,手动构造DynamicTypeA或DynamicTypeB - 将相同的数据再次解组到
BigStruct - 使用手动创建的值修复
DynamicField
...但是当我尝试将数据解组为 BigStruct 时,这将导致在第 5 步中无限递归,这将调用当前正在执行的相同 UnmarshalJSON 函数。
【问题讨论】: