无法从HTTP响应中获取JSON解析的结果。

huangapple go评论90阅读模式
英文:

Can't Get JSON Parsed from HTTP Response

问题

所以,我正在尝试弄清楚如何使以下代码能够正确解析来自https://api.coinmarketcap.com/v1/ticker/ethereum的JSON数据。它似乎没有问题地解码来自http://echo.jsontest.com/key1/value1/key2/value2的响应中的JSON数据,但是当指向CoinMarketCap API时,它只得到空/零值。

package main

import (
	"encoding/json"
	"log"
	"net/http"
)

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)

但都没有成功。非常感谢您的任何建议。

英文:

So, I'm trying to figure out how to get the following code to properly parse the JSON data from https://api.coinmarketcap.com/v1/ticker/ethereum. It seems to have no problem decoding the JSON data in the response from http://echo.jsontest.com/key1/value1/key2/value2, but only gets empty/zero values when pointed at the 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)
}

I suspect it's related to the fact that the JSON at CoinMarketCap is inside a top level JSON array, but I've tried various iterations of things like:

priceData := make([]CoinMarketCapData, 1)

to no avail. Any advice is much appreciated, thanks.

答案1

得分: 0

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

package main

import (
	"encoding/json"
	"log"
	"net/http"
)

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
英文:

The JSON is an array, so you need to pass an array to the Decode method. Also remember to check the returned error.

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

running this prints

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

答案2

得分: 0

你是对的,顶级API响应类型是一个列表,这必须在解组过程中反映出来。修复这个问题的一种方法是将MarketCap响应定义为切片,像这样:

package main

import (
    "encoding/json"
    "log"
    "net/http"
)

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

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

package main

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

哪种方法更适合您取决于您的用例如果您希望结构体反映API响应那么第一种方法似乎更合适如果*MarketCap*是一个实体而API只是访问它的一种方式那么第二种方法更适合我认为

<details>
<summary>英文:</summary>

You&#39;re right, the top level API response type is a list and that has to be reflected in the unmarshalling process. One way to fix this would be to define your *MarketCap* response as slice, like this:

    package main

    import (
        &quot;encoding/json&quot;
        &quot;log&quot;
        &quot;net/http&quot;
    )

    // curl -sLf &quot;https://api.coinmarketcap.com/v1/ticker/ethereum&quot; | JSONGen
    // command line helper: `go get github.com/bemasher/JSONGen`
    type MarketCapResponse []struct {
        AvailableSupply  float64 `json:&quot;available_supply&quot;`
        HVolumeUsd       float64 `json:&quot;24h_volume_usd&quot;`
        Id               string  `json:&quot;id&quot;`
        MarketCapUsd     float64 `json:&quot;market_cap_usd&quot;`
        Name             string  `json:&quot;name&quot;`
        PercentChange1h  float64 `json:&quot;percent_change_1h&quot;`
        PercentChange24h float64 `json:&quot;percent_change_24h&quot;`
        PercentChange7d  float64 `json:&quot;percent_change_7d&quot;`
        PriceBtc         float64 `json:&quot;price_btc&quot;`
        PriceUsd         float64 `json:&quot;price_usd&quot;`
        Rank             int64   `json:&quot;rank&quot;`
        Symbol           string  `json:&quot;symbol&quot;`
        TotalSupply      float64 `json:&quot;total_supply&quot;`
    }

Then the unmarshaling would work just fine. One thing to note is that pointer to a slice is different from the slice. Especially, the pointer does not support indexing, which is why need to dereference it first to access the first item in the list.
 
    func getAndUnmarshal(url string, target interface{}) error {
        var client = &amp;http.Client{}
        req, _ := http.NewRequest(&quot;GET&quot;, url, nil)
        req.Header.Set(&quot;Content-Type&quot;, &quot;application/json&quot;)
        r, _ := client.Do(req)
        defer r.Body.Close()
        return json.NewDecoder(r.Body).Decode(target)
    }

    func main() {
        link := &quot;https://api.coinmarketcap.com/v1/ticker/ethereum&quot;
        resp := new(MarketCapResponse)
        getAndUnmarshal(link, resp)
        log.Printf(&quot;Ethereum Id: %s&quot;, (*resp)[0].Id)
        // prints:
        // 2016/08/22 02:13:23 Ethereum Id: ethereum
    }

----

Another way would be to define a type for a single *MarketCap* and then create a slice as a target when needed:

    package main

	// curl -sLf &quot;https://api.coinmarketcap.com/v1/ticker/ethereum&quot; | jq .[0] | JSONGen
	type MarketCap struct {
		AvailableSupply  float64 `json:&quot;available_supply&quot;`
		HVolumeUsd       float64 `json:&quot;24h_volume_usd&quot;`
		Id               string  `json:&quot;id&quot;`
		MarketCapUsd     float64 `json:&quot;market_cap_usd&quot;`
		Name             string  `json:&quot;name&quot;`
		PercentChange1h  float64 `json:&quot;percent_change_1h&quot;`
		PercentChange24h float64 `json:&quot;percent_change_24h&quot;`
		PercentChange7d  float64 `json:&quot;percent_change_7d&quot;`
		PriceBtc         float64 `json:&quot;price_btc&quot;`
		PriceUsd         float64 `json:&quot;price_usd&quot;`
		Rank             int64   `json:&quot;rank&quot;`
		Symbol           string  `json:&quot;symbol&quot;`
		TotalSupply      float64 `json:&quot;total_supply&quot;`
	}

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

    func main() {
		link := &quot;https://api.coinmarketcap.com/v1/ticker/ethereum&quot;
		resp := make([]MarketCap, 0)
		getAndUnmarshal(link, &amp;resp)
		log.Printf(&quot;Ethereum Id: %s&quot;, resp[0].Id)
        // 2016/08/22 02:13:23 Ethereum Id: ethereum
	}

What is more suitable for you will depend on your use case. If you want the struct to reflect the API response, then the first approach seems more suitable. Is *MarketCap* a thing and the API is just one way to access it, than the second fits better, I believe.

</details>



huangapple
  • 本文由 发表于 2016年8月22日 07:57:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/39069863.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定