Golang

Custom Unmarshal JSON in Golang

While defining a struct for JSON text, we normally define a one to one relationship between the JSON field and the struct variable. We can then unmarshal the JSON into that struct directly using the encoding/json package in the stdlib. What if our data does not completely adhere to the struct we define? If E.g. we have an array within an array assigned to a field.  We could custom unmarshal this JSON data that does not adhere to the struct we define.

E.g.

The crypto currency exchange GDAX market feed API:

https://docs.gdax.com/?javascript#get-product-order-book

Values returned are

{ 
"sequence": 3977318850, 
"bids": [ 
          [ "4625.78", "0.80766325", 3 ] 
        ], 
"asks": [ 
          [ "4625.79", "3.0154341", 3 ] 
        ]
}

The data within the array is:

price, size, order_id

I converted this to a JSON struct using my favorite golang json converter  that has saved me so much time: https://mholt.github.io/json-to-go/

It gave me this golang struct.

type AutoGenerated struct {

Sequence int64 `json:"sequence"`
 Bids [][]interface{} `json:"bids"` 
 Asks [][]interface{} `json:"asks"`

}

Ideally, I would like to define the dataset as below and Unmarshal the API contents into the struct

type Bid struct { 
   Price     string 
   Size      string 
   NumOrders int
}

type OrderBook struct  { 
  Sequence int64 `json:"sequence"` 
  Bids     []Bid `json:"bids"` 
  Asks     []Bid `json:"asks"`
}
body, err := ioutil.ReadAll(resp.Body) 
if err != nil { 
  fmt.Println(err)
}
var orders OrderBook
err = json.Unmarshal(body, &orders) 
if err != nil { 
  fmt.Println(err)
} 

But the Unmarshaller will not automatically unmarshal into the above defined struct. It throws an error : json: cannot unmarshal array into Go value of Bid

Unmarshal calls the UnmarshalJSON method of the value. So to resolve this, all we need to do is define a custom UnmarshalJSON function  for our array to struct conversion:

func (b *Bid) UnmarshalJSON(data []byte) error { 
   var v []interface{} 
   if err := json.Unmarshal(data, &v); err != nil { 
     return err 
   } 
   b.Price, _ = v[0].(string) 
   b.Size, _ = v[1].(string) 
   b.NumOrders = int(v[2].(float64))
   return nil
}

And this will cause the  Unmarshal on the main struct works seamlessly!

The full code is available on github at https://github.com/mariadesouza/go-orderbook.

 

 

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s