I have an problem with decoding an JSON respone. I've tried to solve this problem for a couple off weeks and can't find an working solution online.
This is my Go code that gets the response:
package main
import (
"fmt"
"time"
"strconv"
"encoding/json"
"net/http"
"io"
)
const (
binanceUrl_0 = "https://api.binance.com"
binanceUrl_1 = "https://api1.binance.com"
binanceUrl_2 = "https://api2.binance.com"
binanceUrl_3 = "https://api3.binance.com"
//select which url to use
binanceUrl = binanceUrl_0
binance_GetServerTime = binanceUrl "/api/v3/time"
binance_Ping = binanceUrl "/api/v3/ping"
binance_GetExhangeInfo = binanceUrl "/api/v3/exchangeInfo"
binance_GetExhangeInfo_Symbol = binanceUrl "/api/v3/exchangeInfo?symbol=BNBBTC"
binance_GetKlineData = binanceUrl "/api/v1/klines"
)
type Binance_klines struct {
OpenTime int64
open float32
high float32
low float32
close float32
volume float32
CloseTime int64
QuoteVolume float32
NumTrades int64
TakerBaseVolume float32
TakerQuoteVolume float32
}
func GetKlines_wEndTime(symbol string, interval string, limit int, endTime time.Time) ([]Binance_klines, error) {
var url string
url = binance_GetKlineData "?"
"symbol=" symbol
"&interval=" interval
"&limit=" strconv.FormatInt(int64(limit), 10)
"&endTime=" strconv.FormatInt(endTime.Unix(),10 ) "000"
response, err := http.Get(url)
if err != nil {
return nil, err
}
data, err := respToKlines(response.Body)
if err != nil {
return nil, err
}
response.Body.Close()
return data, nil
}
func respToKlines(data io.Reader) ([]Binance_klines, error) {
var klines []Binance_klines
var decoded []interface{}
decoder := json.NewDecoder(data)
err := decoder.Decode(&decoded)
if err != nil {
return nil, err
}
//Attempt 1:
//kline = (decoded).([]Binance_klines)
//err: invalid operation: (decoded) (variable of type []interface{}) is not an interface
//Attempt 2:
for i:=0; i<len(decoded); i {
to_parse := decoded[i]
fmt.Println("to_parse",to_parse)
//prints: to_parse [1.66427838e 12 20982.91000000 20992.61000000 20977.90000000 20980.95000000 0.68063000 1.664278439999e 12 14282.75833530 57 0.27942000 5864.01792110 0]
var kline Binance_klines
kline = (to_parse).(Binance_klines)
//err: interface conversion: interface {} is []interface {}, not dsBinance.Binance_klines
fmt.Println("kline",kline)
}
return klines, nil
}
func main() {
result, err := GetKlines_wEndTime( "BTCEUR", "1m", 3, time.Now() )
fmt.Println(result, err)
}
This is an reponse I get (converted to string from bytes):
[[1664277480000,"20980.42000000","20984.06000000","20966.57000000","20970.14000000","6.10441000",1664277539999,"128041.93403330",142,"2.97844000","62486.29173860","0"],[1664277540000,"20969.14000000","20976.08000000","20955.69000000","20970.15000000","3.17365000",1664277599999,"66548.64583140",88,"2.39827000","50292.47196580","0"],[1664277600000,"20970.15000000","20970.15000000","20970.15000000","20970.15000000","0.00000000",1664277659999,"0.00000000",0,"0.00000000","0.00000000","0"]]
My question is, what am I doing wrong? When I use a tool like https://mholt.github.io/json-to-go/, it wants me to make an [][]interface{}. But in my for loop you can see that it prints an (in my eyes) a valid: []interface{} but i cannot convert it to an struct of type Binance_Klines. Is something wrong with this line:
kline = (to_parse).(Binance_klines)
Or am I just misunderstanding something? What do I need to change to be able to use the type assertion? Or to just decode it at once to the right struct?
CodePudding user response:
You cannot cast []interface{}
to Binance_klines
. So kline = (to_parse).(Binance_klines)
fails. You have to write the translation yourself.
The data returned is a 2 dimensional array. Here is the json payload you have formatted. The types are a mix of string
and float64
, so Go uses interface{}
to store the values.
[
[
1664277480000,
"20980.42000000",
"20984.06000000",
"20966.57000000",
"20970.14000000",
"6.10441000",
1664277539999,
"128041.93403330",
142,
"2.97844000",
"62486.29173860",
"0"
],
[
1664277540000,
"20969.14000000",
"20976.08000000",
"20955.69000000",
"20970.15000000",
"3.17365000",
1664277599999,
"66548.64583140",
88,
"2.39827000",
"50292.47196580",
"0"
],
[
1664277600000,
"20970.15000000",
"20970.15000000",
"20970.15000000",
"20970.15000000",
"0.00000000",
1664277659999,
"0.00000000",
0,
"0.00000000",
"0.00000000",
"0"
]
]
The json decoder cannot convert this into your Binance_klines
struct. But you can override the default unmarshal behavior yourself.
First I made a type for the sometimes quoted numbers, sometimes not.
type BinanceNumber string
func (b *BinanceNumber) UnmarshalJSON(data []byte) error {
*b = BinanceNumber(strings.Trim(string(data), "\""))
return nil
}
func (b BinanceNumber) Float64() float64 {
f, err := strconv.ParseFloat(string(b), 64)
if err != nil {
panic(err)
}
return f
}
func (b BinanceNumber) Int64() int64 {
i, err := strconv.ParseInt(string(b), 10, 64)
if err != nil {
panic(err)
}
return i
}
Then you override the Binance_klines
unmarshal.
func (b *Binance_klines) UnmarshalJSON(data []byte) error {
var array []BinanceNumber
err := json.Unmarshal(data, &array)
if err != nil {
return err
}
b.OpenTime = array[0].Int64()
b.Open = float32(array[1].Float64())
b.High = float32(array[2].Float64())
b.Low = float32(array[3].Float64())
b.Close = float32(array[4].Float64())
b.Volume = float32(array[5].Float64())
b.CloseTime = array[6].Int64()
b.QuoteVolume = float32(array[7].Float64())
b.NumTrades = array[8].Int64()
b.TakerBaseVolume = float32(array[9].Float64())
b.TakerQuoteVolume = float32(array[10].Float64())
return nil
}
Putting it all together: https://go.dev/play/p/SGGbWEUFxJr.
CodePudding user response:
This part:
var kline Binance_klines
kline = (to_parse).(Binance_klines)
needs to become
var kline Binance_klines
kline.OpenTimer = to_parse[0].(int64)
kline.open = strconv.ParseFloat(to_parse[1].(string), 64)
...
You are not receiving the json representation of your Binance_klines
struct but a slice of any (a mix or numbers and strings).