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.
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.
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"
}
and
{
"some_data": "foo",
"dynamic_field": { "type": "B", "address": "Somewhere" },
"other_data": "bar"
}
The JSON structure is set, I can't change it.
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"`
}
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
}
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
}
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:
- Mark
DynamicField
asjson:"-"
- Implement
UnmarshalJSON
for BigStruct - unmarshal the JSON into a
map[string]interface{}
in theBigStruct
'sUnmarshalJSON
, - inspect the
dynamic_field
value in the map, manually construct eitherDynamicTypeA
orDynamicTypeB
- unmarshal the same data again into
BigStruct
- fixup the
DynamicField
with the manually created values
... 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.