解码仅存在于数组中的JSON响应

huangapple go评论79阅读模式
英文:

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 (
&quot;fmt&quot;
&quot;time&quot;
&quot;strconv&quot;
&quot;encoding/json&quot;
&quot;net/http&quot;
&quot;io&quot;
)
const (
binanceUrl_0 = &quot;https://api.binance.com&quot;
binanceUrl_1 = &quot;https://api1.binance.com&quot;
binanceUrl_2 = &quot;https://api2.binance.com&quot;
binanceUrl_3 = &quot;https://api3.binance.com&quot;
//select which url to use
binanceUrl = binanceUrl_0
binance_GetServerTime 	= binanceUrl + &quot;/api/v3/time&quot;
binance_Ping 			= binanceUrl + &quot;/api/v3/ping&quot;
binance_GetExhangeInfo 	= binanceUrl + &quot;/api/v3/exchangeInfo&quot;
binance_GetExhangeInfo_Symbol 	= binanceUrl + &quot;/api/v3/exchangeInfo?symbol=BNBBTC&quot;
binance_GetKlineData 	= binanceUrl + &quot;/api/v1/klines&quot;
)
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 + &quot;?&quot; +
&quot;symbol=&quot; + symbol + 
&quot;&amp;interval=&quot; + interval + 
&quot;&amp;limit=&quot; + strconv.FormatInt(int64(limit), 10) + 
&quot;&amp;endTime=&quot; + strconv.FormatInt(endTime.Unix(),10 ) + &quot;000&quot;
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(&amp;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&lt;len(decoded); i++ {  
to_parse := decoded[i]
fmt.Println(&quot;to_parse&quot;,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(&quot;kline&quot;,kline)
}
return klines, nil
}
func main() {
result, err := GetKlines_wEndTime( &quot;BTCEUR&quot;, &quot;1m&quot;, 3, time.Now() )
fmt.Println(result, err)
}

This is an reponse I get (converted to string from bytes):

[[1664277480000,&quot;20980.42000000&quot;,&quot;20984.06000000&quot;,&quot;20966.57000000&quot;,&quot;20970.14000000&quot;,&quot;6.10441000&quot;,1664277539999,&quot;128041.93403330&quot;,142,&quot;2.97844000&quot;,&quot;62486.29173860&quot;,&quot;0&quot;],[1664277540000,&quot;20969.14000000&quot;,&quot;20976.08000000&quot;,&quot;20955.69000000&quot;,&quot;20970.15000000&quot;,&quot;3.17365000&quot;,1664277599999,&quot;66548.64583140&quot;,88,&quot;2.39827000&quot;,&quot;50292.47196580&quot;,&quot;0&quot;],[1664277600000,&quot;20970.15000000&quot;,&quot;20970.15000000&quot;,&quot;20970.15000000&quot;,&quot;20970.15000000&quot;,&quot;0.00000000&quot;,1664277659999,&quot;0.00000000&quot;,0,&quot;0.00000000&quot;,&quot;0.00000000&quot;,&quot;0&quot;]]

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 负载。类型是stringfloat64的混合,所以 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,
      &quot;20980.42000000&quot;,
      &quot;20984.06000000&quot;,
      &quot;20966.57000000&quot;,
      &quot;20970.14000000&quot;,
      &quot;6.10441000&quot;,
      1664277539999,
      &quot;128041.93403330&quot;,
      142,
      &quot;2.97844000&quot;,
      &quot;62486.29173860&quot;,
      &quot;0&quot;
   ],
   [
      1664277540000,
      &quot;20969.14000000&quot;,
      &quot;20976.08000000&quot;,
      &quot;20955.69000000&quot;,
      &quot;20970.15000000&quot;,
      &quot;3.17365000&quot;,
      1664277599999,
      &quot;66548.64583140&quot;,
      88,
      &quot;2.39827000&quot;,
      &quot;50292.47196580&quot;,
      &quot;0&quot;
   ],
   [
      1664277600000,
      &quot;20970.15000000&quot;,
      &quot;20970.15000000&quot;,
      &quot;20970.15000000&quot;,
      &quot;20970.15000000&quot;,
      &quot;0.00000000&quot;,
      1664277659999,
      &quot;0.00000000&quot;,
      0,
      &quot;0.00000000&quot;,
      &quot;0.00000000&quot;,
      &quot;0&quot;
   ]
]

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), &quot;\&quot;&quot;))
	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, &amp;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).

huangapple
  • 本文由 发表于 2022年9月27日 19:49:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/73867082.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定