Unmarshal динамический JSON на основе ключа типа - PullRequest
0 голосов
/ 24 ноября 2018

Я ищу решение, которое НЕ включает в себя введение дополнительного «универсального» поля, такого как Value, Data и т. Д., Которое было бы заполнителем для варианта поля.

Iиметь спецификацию 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 dynamic_field, а затем на его основе делает DynamicField либо DynamicTypeA, либо DynamicTypeB.

* 1040.*Но как?Один из способов, который, вероятно, не работает из-за рекурсии:
  • Пометить DynamicField как json:"-"
  • Реализовать UnmarshalJSON для BigStruct
  • unmarshalJSON в map[string]interface{} в BigStruct в UnmarshalJSON,
  • , проверить значение dynamic_field на карте, вручную построить либо DynamicTypeA, либо DynamicTypeB
  • снова демаршируйте те же данные в BigStruct
  • , исправьте DynamicField с вручную созданными значениями

... но это приведет к бесконечной рекурсии на 5-м шаге, когда япопытайтесь распаковать данные в BigStruct, который вызовет ту же функцию UnmarshalJSON, которая выполняется в настоящее время.

1 Ответ

0 голосов
/ 24 ноября 2018
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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...