type MyObj struct {
Field1 string `json:"field_1"`
Field2 int64 `json:"field_2"`
Field3 string `json:"field_3"`
...
FieldK string `json:"field_k"`
FieldN MyInterface `json:"field_n"`
}
我的代码中有一个模型(除了不相关的域细节),看起来像这样。FieldN
字段的想法是支持两种类型,比如MyType1
和MyType2
。这些都有相同的CommonMethod()
,但模型非常不同,所以它不是有一个有更多字段的父类型。
很遗憾,Go无法将JSON解组为接口值。我尝试使用自定义的UnmarshalJSON()
实现,但到目前为止,它看起来真的很尴尬:
func (m *MyObj) UnmarshalJSON(data []byte) error {
out := &MyObj{}
var m map[string]json.RawMessage
if err := json.Unmarshal(data, &m); err != nil {
return err
}
if err := json.Unmarshal(m["field_1"], &out.Field1); err != nil {
return err
}
delete(m, "field_1")
if err := json.Unmarshal(m["field_2"], &out.Field2); err != nil {
return err
}
delete(m, "field_2")
if err := json.Unmarshal(m["field_3"], &out.Field3); err != nil {
return err
}
delete(m, "field_3")
... // from 3 to k-1
if err := json.Unmarshal(m["field_k"], &out.FieldK); err != nil {
return err
}
delete(m, "field_k")
var mt1 MyType1
if err := json.Unmarshal(m["field_n"], &mt1); err == nil {
s.FieldN = &mt1
return nil
}
var mt2 MyType2
if err := json.Unmarshal(m["field_n"], &mt2); err == nil {
s.FieldN = &mt2
return nil
}
return nil
}
这种方法的思想是首先解组所有“静态”值,然后处理接口类型。但在我看来,至少有两个问题:
1.在我的例子中,字段的数量将来可能会增加,代码将变得比现在更重复
1.即使是当前版本也需要检查Mapm
是否有键field_i
,否则我只会得到unexpected end of input
。这甚至更麻烦。
是否有更优雅的方式来执行以下操作:
- 取消封送具有静态类型的所有字段
- 处理唯一的特殊接口类型值
谢谢!
重要更新:
应该注意的是,Field1
有效地定义了FieldN
应该使用哪个具体类型。正如在评论中提到的那样,这应该大大简化了方法,但我仍然在正确的实现方面有一些困难。
2条答案
按热度按时间deyfvvtc1#
使用json.RawMessage捕获对象的变化部分。使用应用程序逻辑中确定的类型对原始消息进行解码。
https://go.dev/play/p/hV3Lgn1RkBz
t8e9dugd2#
此演示基于@mkopriva的建议(
DisallowUnknownFields
),但仍然使用"try one; if failed, try another"
过程。输出: