基于类型密钥解组动态JSON [英] Unmarshal dynamic JSON based on a type key

查看:73
本文介绍了基于类型密钥解组动态JSON的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找不涉及引入其他通用"字段(例如ValueData等)的解决方案,该字段将用作变体字段的占位符.

I'm looking for a solution which DOESN'T involve introducing an additional "generic" field like Value, Data, etc. which would be a placeholder for the variant field.

我有一个JSON规范,它描述了几个大型结构,这些结构大多数都包含简单的值,但有时却是一个结构本身的值,其动态类型取决于某个字段的值.

I have a JSON spec which describes several large structs, which hold mostly simple values, but occasionally a value which is a struct itself, with a dynamic type depending on the value of a certain field.

例如,这两个JSON文档都应解组为同一Go结构:

For example, both these JSON documents should unmarshal to the same Go struct:

{ 
  "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结构,我无法更改.

The JSON structure is set, I can't change it.

Go结构必须如下所示:

The Go struct must look like this:

type BigStruct struct {
  SomeData     string    `json:"some_data"`
  DynamicField Something `json:"dynamic_field"`
  OtherData    string    `json:"other_data"`
}

问题是如何实际执行操作以及Something类型应该是什么.

The question is how to actually do it and what that Something type should be.

我首先将其设置为界面:

I've started by making it an interface:

type Something interface {
  GetType() string
}

并且有几个结构和函数可以配合使用:

And have several structs and funcs to go with it:

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的实例时,我可以这样做:

The reason is, that when I get an instance of BigStruct, I can do this:

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_fieldType字段,然后基于它,将DynamicField设置为DynamicTypeADynamicTypeB.

However, then I got stuck - how could this arrangement work with UnmarshalJSON? I think that BigStruct should implement UnmarshalJSON which would somehow inspect the Type field of the dynamic_field and then based on it, make DynamicField either a DynamicTypeA or DynamicTypeB.

但是如何?一种可能由于递归而不起作用的方法是:

But how? One way which probably doesn't work because of recursion would be:

  • DynamicField标记为json:"-"
  • BigStruct的实现UnmarshalJSON
  • 将JSON解组到BigStructUnmarshalJSON中的map[string]interface{}中,
  • 检查地图中的dynamic_field值,手动构造DynamicTypeADynamicTypeB
  • 再次将相同数据解组到BigStruct
  • 使用手动创建的值修复DynamicField
  • Mark DynamicField as json:"-"
  • Implement UnmarshalJSON for BigStruct
  • unmarshal the JSON into a map[string]interface{} in the BigStruct's UnmarshalJSON,
  • inspect the dynamic_field value in the map, manually construct either DynamicTypeA or DynamicTypeB
  • unmarshal the same data again into BigStruct
  • fixup the DynamicField with the manually created values

...但是在第五步中,当我尝试将数据解组到BigStruct时将导致无限递归,该BigStruct将调用当前正在执行的同一UnmarshalJSON函数.

... but that will lead to infinite recursion in the 5th step when I try to unmarshal the data into a BigStruct which would call the same UnmarshalJSON function which is currently executing.

推荐答案

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上,并声明一个临时类型以避免递归.

If you don't want to, or can't, change the type of the DynamicField you can put the UnmarshalJSON method on the BigStruct and declare a temporary type to avoid recursion.

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

这篇关于基于类型密钥解组动态JSON的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆