Go语言解码Binance接口JSON数组响应失败问题求助
Binance K线接口JSON解码问题修复方案
问题根源
- 结构体字段不可访问:原
Binance_klines结构体字段首字母小写,Go标准库的json包只能识别首字母大写的导出字段,小写字段无法被解码赋值。 - 响应格式不匹配:Binance K线接口返回的是二维数组,每个K线数据是包含12个元素的数组(而非JSON对象),无法直接断言或解码为结构体类型,必须手动解析数组元素。
- 数值类型处理:接口返回的价格、成交量等数据以字符串格式返回(避免浮点精度丢失),需要手动将字符串转换为对应数值类型。
修复方案与代码
修改后的完整代码
package main import ( "encoding/json" "fmt" "net/http" "strconv" "time" ) 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" 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 BinanceKlines struct { OpenTime int64 // 对应数组第0位:开盘时间 Open float32 // 对应数组第1位:开盘价 High float32 // 对应数组第2位:最高价 Low float32 // 对应数组第3位:最低价 Close float32 // 对应数组第4位:收盘价 Volume float32 // 对应数组第5位:成交量 CloseTime int64 // 对应数组第6位:收盘时间 QuoteVolume float32 // 对应数组第7位:报价成交量 NumTrades int64 // 对应数组第8位:交易次数 TakerBaseVolume float32 // 对应数组第9位:主动买入成交量 TakerQuoteVolume float32 // 对应数组第10位:主动买入报价成交量 Ignore string // 对应数组第11位:无用字段,忽略 } // 自定义UnmarshalJSON方法,实现数组到结构体的解码逻辑 func (k *BinanceKlines) UnmarshalJSON(data []byte) error { var arr []interface{} if err := json.Unmarshal(data, &arr); err != nil { return err } // 解析开盘时间(数组第0位,float64转int64) openTime, ok := arr[0].(float64) if !ok { return fmt.Errorf("无效的开盘时间类型") } k.OpenTime = int64(openTime) // 解析开盘价(数组第1位,字符串转float32) openStr, ok := arr[1].(string) if !ok { return fmt.Errorf("无效的开盘价类型") } openVal, err := strconv.ParseFloat(openStr, 32) if err != nil { return err } k.Open = float32(openVal) // 解析最高价(数组第2位) highStr, ok := arr[2].(string) if !ok { return fmt.Errorf("无效的最高价类型") } highVal, err := strconv.ParseFloat(highStr, 32) if err != nil { return err } k.High = float32(highVal) // 解析最低价(数组第3位) lowStr, ok := arr[3].(string) if !ok { return fmt.Errorf("无效的最低价类型") } lowVal, err := strconv.ParseFloat(lowStr, 32) if err != nil { return err } k.Low = float32(lowVal) // 解析收盘价(数组第4位) closeStr, ok := arr[4].(string) if !ok { return fmt.Errorf("无效的收盘价类型") } closeVal, err := strconv.ParseFloat(closeStr, 32) if err != nil { return err } k.Close = float32(closeVal) // 解析成交量(数组第5位) volumeStr, ok := arr[5].(string) if !ok { return fmt.Errorf("无效的成交量类型") } volumeVal, err := strconv.ParseFloat(volumeStr, 32) if err != nil { return err } k.Volume = float32(volumeVal) // 解析收盘时间(数组第6位) closeTime, ok := arr[6].(float64) if !ok { return fmt.Errorf("无效的收盘时间类型") } k.CloseTime = int64(closeTime) // 解析报价成交量(数组第7位) quoteVolumeStr, ok := arr[7].(string) if !ok { return fmt.Errorf("无效的报价成交量类型") } quoteVolumeVal, err := strconv.ParseFloat(quoteVolumeStr, 32) if err != nil { return err } k.QuoteVolume = float32(quoteVolumeVal) // 解析交易次数(数组第8位) numTrades, ok := arr[8].(float64) if !ok { return fmt.Errorf("无效的交易次数类型") } k.NumTrades = int64(numTrades) // 解析主动买入成交量(数组第9位) takerBaseStr, ok := arr[9].(string) if !ok { return fmt.Errorf("无效的主动买入成交量类型") } takerBaseVal, err := strconv.ParseFloat(takerBaseStr, 32) if err != nil { return err } k.TakerBaseVolume = float32(takerBaseVal) // 解析主动买入报价成交量(数组第10位) takerQuoteStr, ok := arr[10].(string) if !ok { return fmt.Errorf("无效的主动买入报价成交量类型") } takerQuoteVal, err := strconv.ParseFloat(takerQuoteStr, 32) if err != nil { return err } k.TakerQuoteVolume = float32(takerQuoteVal) // 忽略第11位无用字段 k.Ignore, _ = arr[11].(string) return nil } func GetKlines_wEndTime(symbol string, interval string, limit int, endTime time.Time) ([]BinanceKlines, error) { 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 } defer response.Body.Close() // 用defer确保响应体被关闭,避免资源泄漏 var klines []BinanceKlines decoder := json.NewDecoder(response.Body) err = decoder.Decode(&klines) if err != nil { return nil, err } return klines, nil } func main() { result, err := GetKlines_wEndTime("BTCEUR", "1m", 3, time.Now()) if err != nil { fmt.Printf("错误:%v\n", err) return } for _, kline := range result { fmt.Printf( "K线数据:开盘时间=%d, 开盘价=%.2f, 收盘价=%.2f, 成交量=%.4f\n", kline.OpenTime, kline.Open, kline.Close, kline.Volume, ) } }
关键修改点
- 结构体字段导出:将所有字段改为首字母大写,确保
json包能访问并赋值。 - 自定义解码方法:实现
UnmarshalJSON方法,专门处理Binance返回的数组格式,逐个解析数组元素并完成类型转换。 - 资源泄漏修复:使用
defer response.Body.Close()确保响应体无论是否出错都会被关闭。 - 简化解码流程:直接将响应解码为
[]BinanceKlines,无需手动遍历[]interface{}再做类型断言。
内容的提问来源于stack exchange,提问作者DanosBandira
相关产品推荐
相关产品推荐

