You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言解码Binance接口JSON数组响应失败问题求助

Binance K线接口JSON解码问题修复方案

问题根源

  1. 结构体字段不可访问:原Binance_klines结构体字段首字母小写,Go标准库的json包只能识别首字母大写的导出字段,小写字段无法被解码赋值。
  2. 响应格式不匹配:Binance K线接口返回的是二维数组,每个K线数据是包含12个元素的数组(而非JSON对象),无法直接断言或解码为结构体类型,必须手动解析数组元素。
  3. 数值类型处理:接口返回的价格、成交量等数据以字符串格式返回(避免浮点精度丢失),需要手动将字符串转换为对应数值类型。

修复方案与代码

修改后的完整代码

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,
		)
	}
}

关键修改点

  1. 结构体字段导出:将所有字段改为首字母大写,确保json包能访问并赋值。
  2. 自定义解码方法:实现UnmarshalJSON方法,专门处理Binance返回的数组格式,逐个解析数组元素并完成类型转换。
  3. 资源泄漏修复:使用defer response.Body.Close()确保响应体无论是否出错都会被关闭。
  4. 简化解码流程:直接将响应解码为[]BinanceKlines,无需手动遍历[]interface{}再做类型断言。

内容的提问来源于stack exchange,提问作者DanosBandira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 05:05:23