为了账号安全,请及时绑定邮箱和手机立即绑定

无法从 HTTP 响应中解析 JSON

无法从 HTTP 响应中解析 JSON

Go
慕尼黑8549860 2022-03-02 13:31:06
因此,我试图弄清楚如何获取以下代码以正确解析来自https://api.coinmarketcap.com/v1/ticker/ethereum的 JSON 数据。解码来自http://echo.jsontest.com/key1/value1/key2/value2的响应中的 JSON 数据似乎没有问题,但在指向 CoinMarketCap API 时只会获得空值/零值。package mainimport(  "encoding/json"  "net/http"  "log")type JsonTest struct {  Key1  string  Key2  string}type CoinMarketCapData struct {  Id               string  Name             string  Symbol           string  Rank             int  PriceUSD         float64  PriceBTC         float64  Volume24hUSD     float64  MarketCapUSD     float64  AvailableSupply   float64  TotalSupply       float64  PercentChange1h   float32  PercentChange24h float32  PercentChange7d   float32}func getJson(url string, target interface{}) error {  client := &http.Client{}  req, _ := http.NewRequest("GET", url, nil)  req.Header.Set("Content-Type", "application/json")  r, err := client.Do(req)  if err != nil {      return err  }  defer r.Body.Close()  return json.NewDecoder(r.Body).Decode(target)}func main() {  //Test with dummy JSON  url1 := "http://echo.jsontest.com/key1/value1/key2/value2"  jsonTest := new(JsonTest)  getJson(url1, jsonTest)  log.Printf("jsonTest Key1: %s", jsonTest.Key1)  //Test with CoinMarketCap JSON  url2 := "https://api.coinmarketcap.com/v1/ticker/ethereum"  priceData := new(CoinMarketCapData)  getJson(url2, priceData)  //Should print "Ethereum Id: ethereum"  log.Printf("Ethereum Id: %s", priceData.Id)}我怀疑这与 CoinMarketCap 的 JSON 位于顶级 JSON 数组中的事实有关,但我尝试了各种迭代,例如:priceData := make([]CoinMarketCapData, 1)无济于事。非常感谢任何建议,谢谢。
查看完整描述

2 回答

?
30秒到达战场

TA贡献1828条经验 获得超6个赞

JSON 是一个数组,因此您需要将一个数组传递给 Decode 方法。还记得检查返回的错误。


package main


import(

  "encoding/json"

  "net/http"

  "log"

)


type CoinMarketCapData struct {

  Id               string

  Name             string

  Symbol           string

  Rank             int

  PriceUSD         float64

  PriceBTC         float64

  Volume24hUSD     float64

  MarketCapUSD     float64

  AvailableSupply   float64

  TotalSupply       float64

  PercentChange1h   float32

  PercentChange24h float32

  PercentChange7d   float32

}


func getJson(url string, target interface{}) error {

  client := &http.Client{}

  req, _ := http.NewRequest("GET", url, nil)

  req.Header.Set("Content-Type", "application/json")

  r, err := client.Do(req)

  if err != nil {

      return err

  }

  defer r.Body.Close()

  return json.NewDecoder(r.Body).Decode(target)

}


func main() {

  //Test with CoinMarketCap JSON

  url2 := "https://api.coinmarketcap.com/v1/ticker/ethereum"

  priceData := make([]CoinMarketCapData, 0)

  err := getJson(url2, &priceData)

  if err != nil {

     log.Printf("Failed to decode json: %v", err)

  } else {

    //Should print "Ethereum Id: ethereum"

    log.Printf("Ethereum Id: %v", priceData[0].Id)

  }

}

运行此打印


2016/08/21 17:15:27 Ethereum Id: ethereum


查看完整回答
反对 回复 2022-03-02
?
哈士奇WWW

TA贡献1799条经验 获得超6个赞

你是对的,顶级 API 响应类型是一个列表,它必须反映在解组过程中。解决此问题的一种方法是将您的MarketCap响应定义为切片,如下所示:


package main


import (

    "encoding/json"

    "log"

    "net/http"

)


// curl -sLf "https://api.coinmarketcap.com/v1/ticker/ethereum" | JSONGen

// command line helper: `go get github.com/bemasher/JSONGen`

type MarketCapResponse []struct {

    AvailableSupply  float64 `json:"available_supply"`

    HVolumeUsd       float64 `json:"24h_volume_usd"`

    Id               string  `json:"id"`

    MarketCapUsd     float64 `json:"market_cap_usd"`

    Name             string  `json:"name"`

    PercentChange1h  float64 `json:"percent_change_1h"`

    PercentChange24h float64 `json:"percent_change_24h"`

    PercentChange7d  float64 `json:"percent_change_7d"`

    PriceBtc         float64 `json:"price_btc"`

    PriceUsd         float64 `json:"price_usd"`

    Rank             int64   `json:"rank"`

    Symbol           string  `json:"symbol"`

    TotalSupply      float64 `json:"total_supply"`

}

然后解组就可以正常工作了。需要注意的一点是指向切片的指针与切片不同。特别是指针不支持索引,这就是为什么需要先取消引用才能访问列表中的第一项。


func getAndUnmarshal(url string, target interface{}) error {

    var client = &http.Client{}

    req, _ := http.NewRequest("GET", url, nil)

    req.Header.Set("Content-Type", "application/json")

    r, _ := client.Do(req)

    defer r.Body.Close()

    return json.NewDecoder(r.Body).Decode(target)

}


func main() {

    link := "https://api.coinmarketcap.com/v1/ticker/ethereum"

    resp := new(MarketCapResponse)

    getAndUnmarshal(link, resp)

    log.Printf("Ethereum Id: %s", (*resp)[0].Id)

    // prints:

    // 2016/08/22 02:13:23 Ethereum Id: ethereum

}

另一种方法是为单个MarketCap定义一个类型,然后在需要时创建一个切片作为目标:


package main


// curl -sLf "https://api.coinmarketcap.com/v1/ticker/ethereum" | jq .[0] | JSONGen

type MarketCap struct {

    AvailableSupply  float64 `json:"available_supply"`

    HVolumeUsd       float64 `json:"24h_volume_usd"`

    Id               string  `json:"id"`

    MarketCapUsd     float64 `json:"market_cap_usd"`

    Name             string  `json:"name"`

    PercentChange1h  float64 `json:"percent_change_1h"`

    PercentChange24h float64 `json:"percent_change_24h"`

    PercentChange7d  float64 `json:"percent_change_7d"`

    PriceBtc         float64 `json:"price_btc"`

    PriceUsd         float64 `json:"price_usd"`

    Rank             int64   `json:"rank"`

    Symbol           string  `json:"symbol"`

    TotalSupply      float64 `json:"total_supply"`

}


func getAndUnmarshal(url string, target interface{}) error {

     ...

}


func main() {

    link := "https://api.coinmarketcap.com/v1/ticker/ethereum"

    resp := make([]MarketCap, 0)

    getAndUnmarshal(link, &resp)

    log.Printf("Ethereum Id: %s", resp[0].Id)

    // 2016/08/22 02:13:23 Ethereum Id: ethereum

}

什么更适合您将取决于您的用例。如果您希望结构反映 API 响应,那么第一种方法似乎更合适。MarketCap是一件事吗?我相信,API 只是访问它的一种方式,比第二种方式更合适。


查看完整回答
反对 回复 2022-03-02
  • 2 回答
  • 0 关注
  • 186 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信