Golang Json unmarshaling with multiple dynamically named fields

Viewed 321

I have the following Json structure that I need to decode and I'm not sure the proper approach for unmarshalling it.

var b = []byte(`{
    "Card:5417871461137421886": {
        "applicationUUID": "5417871461137421886",
        "commit": "test_commit",
        "lastSeen": "test_lastSeen",
        "localUUID": "E4:F5:13:8E:F5:43",
        "progress": "3.5",
        "externalUUID": "test_externalUUID",
        "state": "test_state"
    },
    "Card:5417871461137421886.Color": {
        "applicationUUID": "5417871461137421886",
        "color": "blue"
    }
}`)

My initial approach has been by somehow creating a structure then unmarshalling to the struct, but due to the dynamic nature I'm unsure how to accomplish this. One approach I've tried is using a parallel structure however I have not succeeded at making that work properly also. I'm kind of at a loss for a good way to solve this problem You can see my sample code here https://play.golang.org/p/mzMArgo8ta2

I can not change the structure of the Json because it is being read from an API. Ultimately I do not want or care about the unique field names, since the application ID is always listed as a value. If there would be a simple way to remove all the Card:Id I figure I could just map it like normal.

2 Answers

One option may be to delay parsing until you know the type; something like the following (playground):

devices := make(map[string]json.RawMessage)

err := json.Unmarshal(b, &devices)
if err != nil {
    log.Fatal(err)
}

for key, jsonVal:= range devices {
    if len(key) > 5 && key[:5] == "Card:" {
        var dev Device
        err := json.Unmarshal(jsonVal, &dev)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%#v\n", dev)
    } else if len(key) > 6 && key[:6] == "Color:" {
        var oth Other 
        err := json.Unmarshal(jsonVal, &oth)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%#v\n", oth)
    }
}

Note: Its not clear what you want to do with the data so I'm just printing the structs for now. Its also not really clear how to identify the type of each element (the example in your question differs from the example in your sample code); there may be some documentation for the API that explains this?.

I would do it like this:

package main

import (
   "encoding/json"
   "fmt"
)

const s = `
{
   "Card:5417871461137421886": {
      "applicationUUID": "5417871461137421886",
      "commit": "test_commit",
      "externalUUID": "test_externalUUID",
      "lastSeen": "test_lastSeen",
      "localUUID": "E4:F5:13:8E:F5:43",
      "progress": "3.5",
      "state": "test_state"
   },
   "Card:5417871461137421886.Color": {
      "applicationUUID": "5417871461137421886",
      "color": "blue"
   }
}
`

func main() {
   var m map[string]struct {
      ApplicationUUID, Color, Commit, ExternalUUID, LastSeen, LocalUUID string
      Progress, State string
   }
   json.Unmarshal([]byte(s), &m)
   fmt.Printf("%+v\n", m)
}

but it seems like a map[string]map[string]string would work as well.

Related