Using Golang, I need to unmarshall nested JSON messages with a finite number of structures and a common head. The head contains type information which identifies what detail structures the JSON message contains. What I try to do is to extract the type information from the head and then dynamically select the structure for the details. Stylized example JSON strings look like this:
{"type":"a_number", "data":{"somenumber":1234}}
{"type":"b_string", "data":{"somestring":"a string", "anotherstring": "a second string"}}`
My initial approach was to use structures as follows:
type Head struct {
Type string `json:"type"`
Data interface{} `json:"data"`
}
type A struct {
SomeNumber decimal.Decimal `json:"somenumber"`
}
type B struct {
SomeString string `json:"somestring"`
AnotherString string `json:"anotherstring"`
}
I tried the to use interface.(type) on the data element to check which structure would be applicable, like so:
var msg Head
json.Unmarshal([]byte(jsonString), &msg)
switch v := msg.Data.(type) {
case A:
fmt.Printf("Type is A (%v)", v)
detail := msg.Data.(A)
fmt.Println(detail.SomeNumber)
case B:
fmt.Printf("Type is B (%v)", v)
detail := msg.Data.(B)
fmt.Println(detail.SomeString)
default:
fmt.Printf("I don't know about type %T!\n", v)
}
This obviously did not work, as msg
is of type map[string]interface {}
. My next attempt was then to use something like:
data := msg.Data.(map[string]interface {})
v ,exist := data["somestring"]
if exist {
fmt.Println("somestring: ",v)
}
This works and is OK in this simply case, but in the real case there are many more than 2 structures, and they are themselves nested and fairly complicated.
The only approach that I managed to find was to do create multiple specific structures and use several unmarshalls like this:
type GenericHead struct {
Type string `json:"type"`
Data interface{} `json:"data"`
}
type A struct {
SomeNumber decimal.Decimal `json:"somenumber"`
}
type B struct {
SomeString string `json:"somestring"`
AnotherString string `json:"anotherstring"`
}
type SpecificA struct {
Type string `json:"type"`
Data A `json:"data"`
}
type SpecificB struct {
Type string `json:"type"`
Data B `json:"data"`
}
and then
var msg Head
json.Unmarshal([]byte(jsonString), &msg)
if msg.Type == "a_number" {
var data SpecificA
json.Unmarshal([]byte(jsonString), &data)
} else {
var data SpecificA
json.Unmarshal([]byte(jsonString), &data)
}
Having to define multiple (redundant) structures and unmarshalling several times seems very inefficient and unnecessarily complicated.
What is a more efficient, "best practice" approach to address such a sitution?