Если вы заранее знаете конкретный тип Content
, вы можете реализовать интерфейс json.Unmarshaler
, демаршалируясь в жестко запрограммированный конкретный тип, а затем присвоить результат типу интерфейса.
func (n *Node) UnmarshalJSON(data []byte) error {
var node struct {
Child *Node
C *MyStruct
}
if err := json.Unmarshal(data, &node); err != nil {
return err
}
n.Child = node.Child
n.C = node.C
return nil
}
https://play.golang.org/p/QOJuiLpYrze
Если вам нужно сделать его более гибким, вам нужно как-то сообщить реализации json.Unmarshaler
, какой конкретный тип представляет json. Один из способов сделать это - встроить информацию о типе в json содержимого, например (теперь с помощью интерфейса json.Marshaler
):
func (ms MyStruct) MarshalJSON() ([]byte, error) {
type _MyStruct MyStruct
var out = struct {
Type string `json:"_type"`
_MyStruct
}{
Type: "MyStruct",
_MyStruct: _MyStruct(ms),
}
return json.Marshal(out)
}
Обновить демаршалер Node
реализация соответственно:
func (n *Node) UnmarshalJSON(data []byte) error {
var node struct {
Child *Node
C json.RawMessage
}
if err := json.Unmarshal(data, &node); err != nil {
return err
}
n.Child = node.Child
if len(node.C) > 0 && string(node.C) != `null` {
var _type struct {
Type string `json:"_type"`
}
if err := json.Unmarshal([]byte(node.C), &_type); err != nil {
return err
}
c := newContent[_type.Type]()
if err := json.Unmarshal([]byte(node.C), c); err != nil {
return err
}
n.C = c
}
return nil
}
и определите newContent
как карту, значениями которой являются функции, возвращающие новые экземпляры конкретного типа:
var newContent = map[string]func() Content{
"MyStruct": func() Content { return new(MyStruct) },
// ...
}
Попробуйте на игровой площадке: https://play.golang.org/p/u9L0VxEG4dT