首页 > 解决方案 > 将 JSON 数组 API 响应转换为结构

问题描述

使用 Go,我希望查询 API 端点并将结果输出到 Gorm SQLite DB。这已经在(此处)之前完成,但我需要自己编写代码。

API 端点返回一个 JSON 数组,对于数组中的每笔交易,我想将其放入一个结构中,然后将其作为行添加到 SQLite 数据库中。

结构定义如下:

type Trade struct {
    TradeID      int64  `json:"id"`
    Price        string `json:"price"`
    Qty          string `json:"qty"`
    QuoteQty     string `json:"quoteQty"`
    Time         int64  `json:"time"`
    IsBuyerMaker bool   `json:"isBuyerMaker"`
    IsBestMatch  bool   `json:"isBestMatch"`
}

这些类型可能看起来很奇怪,但通过使用 PowerShell 和以下代码确定:

PS C:\Git> $body = @{"symbol" = "ETHEUR";"limit" = 1000}
PS C:\Git> $response = Invoke-RestMethod https://api.binance.com/api/v3/trades -Body $body
PS C:\Git> $response[0] | gm

   TypeName: System.Management.Automation.PSCustomObject

Name         MemberType   Definition
----         ----------   ----------
Equals       Method       bool Equals(System.Object obj)
GetHashCode  Method       int GetHashCode()
GetType      Method       type GetType()
ToString     Method       string ToString()
id           NoteProperty long id=21731777
isBestMatch  NoteProperty bool isBestMatch=True
isBuyerMaker NoteProperty bool isBuyerMaker=True
price        NoteProperty string price=3539.03000000
qty          NoteProperty string qty=0.28600000
quoteQty     NoteProperty string quoteQty=1012.16258000
time         NoteProperty long time=1620822731248

所以,我到目前为止的Go函数如下:


func getTrade(symbol string, limit int) {
    uri := fmt.Sprintf("https://api.binance.com/api/v3/trades?symbol=%v&limit=%v", symbol, limit)

    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    if err != nil {
        panic("failed to connect database")
    }

    // Create the fields in the database based on the Trade Struct
    db.AutoMigrate(&Trade{})

    // Query Binance API endpoint
    response, err := http.Get(uri)

    // Log if an error occurred
    if err != nil {
        log.Fatalln(err)
    }

    // Defer closing object
    defer response.Body.Close()

    // Create trade struct
    trade := Trade{}

从这里我尝试了几种将响应转换为结构的各种方法,但失败了。这些方法包括:

// Unmarshal bytes to data struct
    _ = json.Unmarshal([]byte(responseData), &trade)
var myClient = &http.Client{Timeout: 10 * time.Second}

func getJson(url string, target interface{}) error {
    response, err := myClient.Get(url)
    if err != nil {
        return err
    }
    defer response.Body.Close()

    return json.NewDecoder(response.Body).Decode(target)
}
// Iterate through trades returned (this doesn't work)
    for _, trade := range responseData {
        tradeVariable := Trade{}
        tradeVariable.TradeID = trade.id
        fmt.Println(tradeVariable.TradeID)
    }

我感觉真的被困住了,就像我错过了与编组有关的东西,有人可以帮我吗?

标签: arraysjsongo

解决方案


由于 JSON 响应是一个数组,因此您需要将其解组为一个结构切片。确保您还使用了正确的 Go 类型。用于float64JSON 数字。

type Trade struct {
    TradeID      float64 `json:"id"`
    Price        string  `json:"price"`
    Qty          string  `json:"qty"`
    QuoteQty     string  `json:"quoteQty"`
    Time         float64 `json:"time"`
    IsBuyerMaker bool    `json:"isBuyerMaker"`
    IsBestMatch  bool    `json:"isBestMatch"`
}

func main() {
    symbol := "ETHEUR"
    limit := 1000
    uri := fmt.Sprintf("https://api.binance.com/api/v3/trades?symbol=%v&limit=%v", symbol, limit)
    response, err := http.Get(uri)
    if err != nil {
        fmt.Println(err)
    }
    body, err := ioutil.ReadAll(response.Body)
    if err != nil {
        fmt.Println(err)
    }
    defer response.Body.Close()
    trade := []Trade{}
    err = json.Unmarshal([]byte(body), &trade)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(trade)
}

推荐阅读