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.


The crypto currency exchange GDAX market feed API:


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 { 
var orders OrderBook
err = json.Unmarshal(body, &orders) 
if err != nil { 

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 to work seamlessly!

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