Go reading a nested JSON without structs the easy way…
Let’s face it sometimes all we need is just to get something out of a JSON response without actually needing to go all the way and create structs to reflect its internals.
The quick and dirty solution to this is a map with a string as a key and an interface{} (think Object in any other language) which can then be accessed with mymap[“key”]
and if needed cast on the fly like this: mymap[“key”].(string)
Problem is this all works super easy and out of the box with a one level json. The issue is when you would need to access something more internal. Let’s take this JSON example:
{
"data": {
"gid": "1140824417098551",
"resource_type": "task",
"created_at": "2019-09-20T07:17:05.161Z",
"modified_at": "2019-09-20T07:17:05.161Z",
"name": "test from postman",
"resource_subtype": "default_task",
"notes": "notes from postman",
"assignee": {
"gid": "1139046208196844",
"resource_type": "user",
"name": "Fabio Russo"
},
"completed": false,
"assignee_status": "inbox",
"completed_at": null,
"due_on": null,
"due_at": null,
"projects": [
{
"gid": "1139411274392065",
"resource_type": "project",
"name": "test"
}
],
"start_on": null,
"tags": [],
}
}
With some omissis this is a typical response when adding a task with the Asana API. Let’s say I just need to print out or store the task id (in this json “gid”).
If I just do it like this:
var response map[string]interface{}
respBody, err := ioutil.ReadAll(resp.Body)
if err = json.Unmarshal(respBody, &response); err != nil {
//error code here
}
Now let’s say I’d love to do something that other languages allow such as: responseID := response["data"]["gid"]
But that’s asking too much. And if I do it in two steps: responseData := response["data"]
I can’t just do responseGID := responseData["gid"]
because responseData is an interface{} and not a map[string]interface{} aka a json object we can inspect.
The solution is simple: just cast the responseData into what it really is: a map[string]interface{} because we know it is.
I end up with a response object that I can use like this:
responseData := response["data"].(map[string]interface{})
taskID := responseData["gid"].(string)
Which will totally work as long as we know the json structure in advance (which we would need to even in the case we’d be using structs for the job).