Dynamic JSON parsing into Go structs

Mr.T
2 min readApr 8, 2021

--

Go and JSON parsing have been used extensively and one can find plethora of resources online when it comes to parsing simple straightforward JSON data into Go structs. Though, JSON parsing in Go can be tricky sometimes. If the JSON schema is dynamic, meaning we have a key which can be string, int, bool, array parsing into a Go struct becomes tricky. Hence, we use interface for such cases.

Following is an example of dynamic JSON and the corresponding logic for unmarshaling JSON where the values can be dynamic for the same key.

var data = []byte(`{

"contentVersion": "1.0.0.0",
"paths" : [
{
"ParameterReference": "someValue"
},
{
"ParameterReference": "someOtherValue",
"EnableHealthDimensions" : "true"
},
"just a string"
],
"parameters": {
"label": { "value": "label" }
},
"secrets" :[
{
"TargetReference":"SERviceConfiGURatiONLiNK",
"Replacements":
{
"__Secret_Sample__" :
{
"SecretId":"secretID",
"EncryptWith" :"encryptWithValue"
},
"__Another_Secret_Sample__" :
{
"SecretId":"anotherSecretValue",
"EncryptWith" :"anotherEncryptWithValue"
},
"__Storage_ConnectionString__" : "someConnectionString"
}
},
{
"TargetReference":"None",
"Certificates":[
{
"Name":"AnotherSampleCert",
"ContentReference" : "contentReference"
}
]
}
]
}`)
type Parameter struct{ Value string }type Parameters map[string]Parametertype Replacements map[string]Secrettype Secret struct {
SecretId string
EncryptWith string
StringValue string
}
type Path struct {
ParameterReference string
StringValue string
}
func (p *Path) UnmarshalJSON(data []byte) error {
if len(data) == 0 || string(data) == "null" {
return nil
}
if data[0] == '"' {
return json.Unmarshal(data, &p.StringValue)
}
if data[0] == '{' {
type P Path
return json.Unmarshal(data, (*P)(p))
}
return nil
}
func (s *Secret) UnmarshalJSON(data []byte) error {
if len(data) == 0 || string(data) == "null" {
return nil
}
if data[0] == '"' { // string
return json.Unmarshal(data, &s.StringValue)
}
if data[0] == '{' { // object
type S Secret
return json.Unmarshal(data, (*S)(s))
}
// do something else, or return error ...
return nil
}
func main() {
var obj struct {
Paths []Path
Parameters Parameters
Secrets []struct {
Replacements Replacements
}
}
if err := json.Unmarshal(data, &obj); err != nil {
panic(err)
}
fmt.Println(obj.Paths)}

By implementing custom Unmarshal methods for our structs, we have more control over the deserializing of a dynamic JSON.

--

--