无法从HTTP响应中获取JSON

时间:2016-08-21 23:57:51

标签: json go http-get

因此,我试图弄清楚如何从https://api.coinmarketcap.com/v1/ticker/ethereum获取以下代码来正确解析JSON数据。在http://echo.jsontest.com/key1/value1/key2/value2的响应中解码JSON数据似乎没有问题,但只有在指向CoinMarketCap API时才会获得空/零值。

package main

import(
  "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 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

您是对的,顶级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只是一种访问它的方式,而不是第二种方式更合适,我相信。