4

I have JSON that looks like this:

{
    "a": [
          [
              "12.58861425",
              10.52046452
          ],
          [
              "12.58861426",
              4.1073
          ]
        ]
    "b": [
          [
              "12.58861425",
              10.52046452
          ],
          [
              "12.58861426",
              4.1073
          ]
        ]
    "c": "true"
    "d": 1234
}
        

I want to unmarshall this into a struct I created:

type OrderBook struct {
 A [][2]float32 `json:"a"`
 B [][2]float32 `json:"b"`
        C string `json:"c"`
 D uint32 `json:"d"`
}

//Note this won't work because the JSON array contains a string and a float value pair rather than only floats.

Normally I would then convert this JSON into a struct in Golang as so:

orders := new(OrderBook)
err = json.Unmarshal(JSON, &orders)

Since I can't define the type struct to match the JSON this won't work. Doing a bit of reading, I think Unmarshal-ing into an interface, and then using the interface data through type checking may be a solution.

Am I going in the right direction with this?
A template showing how I might go about using the data once it's in the interface would really help.

Nik Kunkel
  • 335
  • 7
  • 14
  • I would recommend against unmarshalling into an interface, there are simpler solutions to the same problem. Here's a couple suggestions that might get you moving towards an appropriate solution. So one option is to provide custom implementation of `UnmarshalJSON` (https://gist.github.com/mdwhatcott/8dd2eef0042f7f1c0cd8) where you would provide custom logic to convert the strings to doubles. The other would be to keep everything the same but define just those inner arrays as an `[]interface{}`. You could also convert after this, just I would recommend being as specific as possible there. – evanmcdonnal Aug 09 '16 at 22:28
  • For me, better for you to make standard JSON instead. Is the one who generate JSON is external program or your program? – Apin Aug 10 '16 at 04:17
  • I agree, if you have control over the structure of the json, that would be better still. But, as is usually the case you don't, and so you can use my answer. – Dale Aug 10 '16 at 04:40
  • @Apin Unfortunately these are external services generating the JSON. – Nik Kunkel Aug 10 '16 at 07:08

1 Answers1

9

Why not just leave out declaring the types of the A and B slices like so?

data := []byte(`{"a": [["12.58861425",10.52046452],["12.58861426",4.1073]],"b": [["12.58861425",10.52046452],["12.58861426",4.1073]],"c": "true","d": 1234}`)

type OrderBook struct {
    A [][2]interface{} `json:"a"`
    B [][2]interface{} `json:"b"`
    C string           `json:"c"`
    D uint32           `json:"d"`
}
orders := new(OrderBook)
err := json.Unmarshal(data, &orders)
if err != nil {
    fmt.Printf(err.Error())
} else {
    fmt.Printf("%s\n", orders.A[0][0].(string))     
    fmt.Printf("%f\n", orders.A[0][1].(float64))
}

There's an example in the playground: https://play.golang.org/p/0MUY-yOYII

I must disagree with evanmcdonnal, there are certainly use cases where where rolling your own Unmarshaller makes sense, this I do not believe is one of them. It doesn't get much simpler than what is shown here.

Dale
  • 1,220
  • 2
  • 10
  • 20