POSTS
JSON Parsing With Golang
Challenge:
When using Go, we can’t be quite as “loosey-goosey” with parsing JSON compared to using Ruby or JS. The challenge/lesson I am trying to solve/learn is how to parse API responses that arent always straight forward or a 1:1 mapping to a given data struct in go. By leveraging json.RawMessage in our code, we have a tool at our disposal to make this scenario much easier to deal with.
Credit for this “Note to self” goes to both:
- Adam Ng: https://www.socketloop.com/tutorials/golang-marshal-and-unmarshal-json-rawmessage-struct-example
- Aiden Coyle: https://www.youtube.com/watch?v=XsL7ikhjNJw
There are many great examples out there in addition to these - but these 2 made the most sense to me as I learn more about Go.
Opening Definitions
Note the structs used for this code to marshal & store data
package main
import (
"encoding/json"
"fmt"
)
// Computer ...
type Computer struct {
Type string `json:"type"`
Model string `json:"model"`
}
// Mammal ...
type Mammal struct {
Type string `json:"type"`
Name string `json:"name"`
}
// Message ...
type Message struct {
Type string `json:"type"`
Data json.RawMessage
}
// Response ...
type Response struct {
Mammal *Mammal `json:"mammal,omitempty"`
Computer *Computer `json:"computer,omitempty"`
}
By using pointers in the Response struct as well as setting omitempty, we don’t have to worry about including any additonal attributes not set when marshalling the response struct to JSON.
Main execution
Here, we will loop through an array containing 2 json data structures.
func main() {
var a [2][]byte
a[0] = []byte(`{
"type": "computer",
"data": {
"type": "droid",
"model": "C3PO"
}}`)
a[1] = []byte(`{
"type": "mammal",
"data": {
"type": "homosapien",
"name": "Fred"
}}`)
for i := 0; i < len(a); i++ {
if err := parseMessage(a[i]); err != nil {
fmt.Println(err)
}
}
}
Parsing logic
For a given slice of bytes (JSON data), our goal is to use data at a higher level in the payload (type in this case) to determine how to parse the remainder of the JSON payload.
func parseMessage(JSONData []byte) error {
var m Message
var r Response
// Unmarshal JSONData to Message struct first
if err := json.Unmarshal(JSONData, &m); err != nil {
return err
}
// Based on the "type" value, unmarshal accordingly
switch m.Type {
case "computer":
var o Computer
if err := json.Unmarshal([]byte(m.Data), &o); err != nil {
return err
}
fmt.Printf("I am a %s. My model is: %s.\n", o.Type, o.Model)
r.Computer = &o
case "mammal":
var o Mammal
if err := json.Unmarshal([]byte(m.Data), &o); err != nil {
return err
}
fmt.Printf("I am a %s. My name is: %s.\n", o.Type, o.Name)
r.Mammal = &o
default:
fmt.Println("unable to unmarshal JSON data or differentiate the type")
}
j, err := json.Marshal(r)
if err != nil {
return err
}
fmt.Println(string(j))
return nil
}