Home > Back-end >  Decoding a JSON response that exists only out of an array
Decoding a JSON response that exists only out of an array

Time:09-27

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).

  • Related