英文:
Decoding a JSON response that exists only out of an array
问题
我有一个解码JSON响应的问题。我已经尝试解决这个问题几个星期了,但在网上找不到有效的解决方案。
这是我的Go代码,用于获取响应:
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"
//选择要使用的URL
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
}
for i:=0; i<len(decoded); i++ {
to_parse := decoded[i]
fmt.Println("to_parse",to_parse)
var kline Binance_klines
kline = to_parse.(Binance_klines)
fmt.Println("kline",kline)
}
return klines, nil
}
func main() {
result, err := GetKlines_wEndTime("BTCEUR", "1m", 3, time.Now())
fmt.Println(result, err)
}
这是我得到的响应(从字节转换为字符串):
[[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"]]
我的问题是,我做错了什么?当我使用类似https://mholt.github.io/json-to-go/的工具时,它要求我创建一个[][]interface{}
。但是在我的循环中,你可以看到它打印出一个(在我看来)有效的[]interface{}
,但我无法将其转换为类型为Binance_Klines
的结构体。这一行有问题吗?
kline = to_parse.(Binance_klines)
还是我只是误解了一些东西?我需要做出什么改变才能使用类型断言?或者直接将其解码为正确的结构体?
英文:
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?
答案1
得分: 2
你不能将[]interface{}
转换为Binance_klines
。所以kline = (to_parse).(Binance_klines)
会失败。你需要自己编写转换代码。
返回的数据是一个二维数组。以下是你格式化后的 JSON 负载。类型是string
和float64
的混合,所以 Go 使用interface{}
来存储这些值。
[
[
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"
]
]
JSON 解码器无法将其转换为你的Binance_klines
结构体。但你可以自己重写默认的解组行为。
首先,我创建了一个类型,用于处理有时带引号的数字。
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
}
然后,你重写Binance_klines
的解组方法。
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
}
将它们整合在一起:https://go.dev/play/p/SGGbWEUFxJr。
英文:
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.
答案2
得分: 0
这部分代码需要进行修改:
var kline Binance_klines
kline.OpenTimer = to_parse[0].(int64)
kline.open, _ = strconv.ParseFloat(to_parse[1].(string), 64)
...
你收到的不是 Binance_klines
结构体的 JSON 表示,而是一个包含数字和字符串的切片。
英文:
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).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论