英文:
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'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 (
"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"`
}
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 = &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
}
----
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 "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
}
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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论