I am not unable to cast the following json
in a struct in Golang, received from Kraken
API:
{
"error": [],
"result": {
"LINKUSD": {
"asks": [
["2.049720", "183.556", 1576323009],
["2.049750", "555.125", 1576323009],
["2.049760", "393.580", 1576323008],
["2.049980", "206.514", 1576322995]
],
"bids": [
["2.043800", "20.691", 1576322350],
["2.039080", "755.396", 1576323007],
["2.036960", "214.621", 1576323006],
["2.036930", "700.792", 1576322987]
]
}
}
}
Using json-to-go
, he gives me the following struct:
type AutoGenerated struct {
Error []interface{} `json:"error"`
Result struct {
LINKUSD struct {
Asks [][]interface{} `json:"asks"`
Bids [][]interface{} `json:"bids"`
} `json:"LINKUSD"`
} `json:"result"`
}
Obviously, i can't hardocode the LINKUSD
cause it will change for every currency pairs.
I've created two structure for accomplish the task, but i'm not able to cast the result in the struct.
type BitfinexOrderBook struct {
Pair string `json:"pair"`
Asks []BitfinexOrder `json:"asks"`
Bids []BitfinexOrder `json:"bids"`
}
type BitfinexOrder struct {
Price string
Volume string
Timestamp time.Time
}
My first attempt was to use reflection. Reading the JSON data posted above, i'm able to retrieve an interface that contains the asks
and bids
list.
// Just used as a divisor
const div string = " ---------------------------------"
func test(data []byte) error {
var err error
// Creating the maps for the JSON data
m := map[string]interface{}{}
// Parsing/Unmarshalling the json readed from the file
err = json.Unmarshal(data, &m)
if err != nil {
log.Println("Error unmarshalling data: " + err.Error())
return err
}
// Extract the "result" only
a := reflect.ValueOf(m["result"])
if a.Kind() == reflect.Map {
key := a.MapKeys()[0] // Extract the key -> LINKUSD
log.Println("KEY: ", key, div)
strct := a.MapIndex(key) // Extract the value -> asks and bids array
log.Println("MAP: ", strct, div)
m, _ := strct.Interface().(map[string]interface{})
log.Println("Asks: ", m["asks"], div) // This will print [[value, value, value] ...] related to asks
log.Println("Bids: ", m["bids"], div) // This will print [[value, value, value] ...] related to bids
// Parse the interface into a []byte
asks_data, err := json.Marshal(m["asks"])
log.Println("OK: ", err, div)
log.Println("ASKS: ", string(asks_data), div)
// Tried without array to (datastructure.BitfinexOrder)
var asks []datastructure.BitfinexOrder
err = json.Unmarshal(asks_data, &asks)
log.Println("ERROR: ", err, div)
log.Println("UNMARSHAL: ", asks, div)
}
return errors.New("UNABLE_PARSE_VALUE")
}
The two print below m, _ := strct.Interface().(map[string]interface{})
will show the following similar data that i'm not able to cast due to the fact that are of interface
type:
[[2.049720 183.556 1.576323009e+09] [2.049750 555.125 1.576323009e+09] [2.049760 393.580 1.576323008e+09] [2.049980 206.514 1.576322995e+09]]
But i'm not able to unmarshal the data.
So i've tried with a different function provided by @chmike
:
// UnmarshalJSON decode a BifinexOrder.
func UnmarshalJSON(data []byte) (datastructure.BitfinexOrder, error) {
var packedData []json.Number
var order datastructure.BitfinexOrder
err := json.Unmarshal(data, &packedData)
if err != nil {
return order, err
}
order.Price = packedData[0].String()
order.Volume = packedData[1].String()
t, err := packedData[2].Int64()
if err != nil {
return order, err
}
order.Timestamp = time.Unix(t, 0)
return order, nil
}
But i receive the following error:
json: cannot unmarshal array into Go value of type json.Number
Some tips?
NOTE: My previous questions was closed as a duplicate of "Unmarshal 2 different structs in a slice". But, if you read the questions, i'm not dealing with two different structs... I'm dealing with a json that contains a key that i does not know at prior. I'm not able to marshal BitfinexOrder too
Solution found from @mkopriva and @chmike:
Thank you guys!
package main
import (
"fmt"
"time"
"encoding/json"
)
var data = []byte(`{
"error": [],
"result": {
"LINKUSD": {
"asks": [
["2.049720", "183.556", 1576323009],
["2.049750", "555.125", 1576323009],
["2.049760", "393.580", 1576323008],
["2.049980", "206.514", 1576322995]
],
"bids": [
["2.043800", "20.691", 1576322350],
["2.039080", "755.396", 1576323007],
["2.036960", "214.621", 1576323006],
["2.036930", "700.792", 1576322987]
]
}
}
}`)
type Response struct {
Error []interface{} `json:"error"`
Result map[string]Order `json:"result"`
}
type Order struct {
Asks []BitfinexOrder `json:"asks"`
Bids []BitfinexOrder `json:"bids"`
}
type BitfinexOrder struct {
Price string
Volume string
Timestamp time.Time
}
// UnmarshalJSON decode a BifinexOrder.
func (b *BitfinexOrder) UnmarshalJSON(data []byte) error {
var packedData []json.Number
err := json.Unmarshal(data, &packedData)
if err != nil {
return err
}
b.Price = packedData[0].String()
b.Volume = packedData[1].String()
t, err := packedData[2].Int64()
if err != nil {
return err
}
b.Timestamp = time.Unix(t, 0)
return nil
}
func main() {
res := &Response{}
if err := json.Unmarshal(data, res); err != nil {
panic(err)
}
for key, value := range res.Result {
fmt.Println(key)
for i, ask := range value.Asks {
fmt.Printf("Asks[%d] = %#v\n", i, ask)
}
for i, bid := range value.Bids {
fmt.Printf("Bids[%d] = %#v\n", i, bid)
}
}
}