我正在尝试从网络读取 JSON 数据,但该代码返回空结果。我不确定我在这里做错了什么。
package main
import "os"
import "fmt"
import "net/http"
import "io/ioutil"
import "encoding/json"
type Tracks struct {
Toptracks []Toptracks_info
}
type Toptracks_info struct {
Track []Track_info
Attr []Attr_info
}
type Track_info struct {
Name string
Duration string
Listeners string
Mbid string
Url string
Streamable []Streamable_info
Artist []Artist_info
Attr []Track_attr_info
}
type Attr_info struct {
Country string
Page string
PerPage string
TotalPages string
Total string
}
type Streamable_info struct {
Text string
Fulltrack string
}
type Artist_info struct {
Name string
Mbid string
Url string
}
type Track_attr_info struct {
Rank string
}
func get_content() {
// json data
url := "http://ws.audioscrobbler.com/2.0/?method=geo.gettoptracks&api_key=c1572082105bd40d247836b5c1819623&format=json&country=Netherlands"
res, err := http.Get(url)
if err != nil {
panic(err.Error())
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err.Error())
}
var data Tracks
json.Unmarshal(body, &data)
fmt.Printf("Results: %v\n", data)
os.Exit(0)
}
func main() {
get_content()
}
理想的方式是不使用ioutil.ReadAll
,而是直接在阅读器上使用解码器。这是一个很好的函数,它获取一个 url 并将其响应解码为 target
结构。
var myClient = &http.Client{Timeout: 10 * time.Second}
func getJson(url string, target interface{}) error {
r, err := myClient.Get(url)
if err != nil {
return err
}
defer r.Body.Close()
return json.NewDecoder(r.Body).Decode(target)
}
示例使用:
type Foo struct {
Bar string
}
func main() {
foo1 := new(Foo) // or &Foo{}
getJson("http://example.com", foo1)
println(foo1.Bar)
// alternately:
foo2 := Foo{}
getJson("http://example.com", &foo2)
println(foo2.Bar)
}
您不应该在生产中使用默认的 *http.Client
结构,因为这个答案最初证明了! (这是 http.Get
/etc 调用的内容)。原因是默认客户端没有设置超时;如果远程服务器没有响应,那么您将度过糟糕的一天。
您的问题是数据 structs
中的切片声明(Track
除外,它们不应该是切片......)。获取的 json 文件中的一些相当愚蠢的字段名加剧了这种情况,这些字段名可以通过 structtags 进行修复,请参阅 godoc。
下面的代码成功解析了 json。如果您还有其他问题,请告诉我。
package main
import "fmt"
import "net/http"
import "io/ioutil"
import "encoding/json"
type Tracks struct {
Toptracks Toptracks_info
}
type Toptracks_info struct {
Track []Track_info
Attr Attr_info `json: "@attr"`
}
type Track_info struct {
Name string
Duration string
Listeners string
Mbid string
Url string
Streamable Streamable_info
Artist Artist_info
Attr Track_attr_info `json: "@attr"`
}
type Attr_info struct {
Country string
Page string
PerPage string
TotalPages string
Total string
}
type Streamable_info struct {
Text string `json: "#text"`
Fulltrack string
}
type Artist_info struct {
Name string
Mbid string
Url string
}
type Track_attr_info struct {
Rank string
}
func perror(err error) {
if err != nil {
panic(err)
}
}
func get_content() {
url := "http://ws.audioscrobbler.com/2.0/?method=geo.gettoptracks&api_key=c1572082105bd40d247836b5c1819623&format=json&country=Netherlands"
res, err := http.Get(url)
perror(err)
defer res.Body.Close()
decoder := json.NewDecoder(res.Body)
var data Tracks
err = decoder.Decode(&data)
if err != nil {
fmt.Printf("%T\n%s\n%#v\n",err, err, err)
switch v := err.(type){
case *json.SyntaxError:
fmt.Println(string(body[v.Offset-40:v.Offset]))
}
}
for i, track := range data.Toptracks.Track{
fmt.Printf("%d: %s %s\n", i, track.Artist.Name, track.Name)
}
}
func main() {
get_content()
}
您需要结构中的大写属性名称才能被 json 包使用。
大写的属性名称是 exported properties
。不导出小写属性名称。
您还需要通过引用 (&data
) 传递您的数据对象。
package main
import "os"
import "fmt"
import "net/http"
import "io/ioutil"
import "encoding/json"
type tracks struct {
Toptracks []toptracks_info
}
type toptracks_info struct {
Track []track_info
Attr []attr_info
}
type track_info struct {
Name string
Duration string
Listeners string
Mbid string
Url string
Streamable []streamable_info
Artist []artist_info
Attr []track_attr_info
}
type attr_info struct {
Country string
Page string
PerPage string
TotalPages string
Total string
}
type streamable_info struct {
Text string
Fulltrack string
}
type artist_info struct {
Name string
Mbid string
Url string
}
type track_attr_info struct {
Rank string
}
func get_content() {
// json data
url := "http://ws.audioscrobbler.com/2.0/?method=geo.gettoptracks&api_key=c1572082105bd40d247836b5c1819623&format=json&country=Netherlands"
res, err := http.Get(url)
if err != nil {
panic(err.Error())
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err.Error())
}
var data tracks
json.Unmarshal(body, &data)
fmt.Printf("Results: %v\n", data)
os.Exit(0)
}
func main() {
get_content()
}
json.Unmarshal
(到 var data interface{}
)的结果与您的 Go 类型和变量声明不直接匹配。例如,
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
)
type Tracks struct {
Toptracks []Toptracks_info
}
type Toptracks_info struct {
Track []Track_info
Attr []Attr_info
}
type Track_info struct {
Name string
Duration string
Listeners string
Mbid string
Url string
Streamable []Streamable_info
Artist []Artist_info
Attr []Track_attr_info
}
type Attr_info struct {
Country string
Page string
PerPage string
TotalPages string
Total string
}
type Streamable_info struct {
Text string
Fulltrack string
}
type Artist_info struct {
Name string
Mbid string
Url string
}
type Track_attr_info struct {
Rank string
}
func get_content() {
// json data
url := "http://ws.audioscrobbler.com/2.0/?method=geo.gettoptracks&api_key=c1572082105bd40d247836b5c1819623&format=json&country=Netherlands"
url += "&limit=1" // limit data for testing
res, err := http.Get(url)
if err != nil {
panic(err.Error())
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err.Error())
}
var data interface{} // TopTracks
err = json.Unmarshal(body, &data)
if err != nil {
panic(err.Error())
}
fmt.Printf("Results: %v\n", data)
os.Exit(0)
}
func main() {
get_content()
}
输出:
Results: map[toptracks:map[track:map[name:Get Lucky (feat. Pharrell Williams) listeners:1863 url:http://www.last.fm/music/Daft+Punk/_/Get+Lucky+(feat.+Pharrell+Williams) artist:map[name:Daft Punk mbid:056e4f3e-d505-4dad-8ec1-d04f521cbb56 url:http://www.last.fm/music/Daft+Punk] image:[map[#text:http://userserve-ak.last.fm/serve/34s/88137413.png size:small] map[#text:http://userserve-ak.last.fm/serve/64s/88137413.png size:medium] map[#text:http://userserve-ak.last.fm/serve/126/88137413.png size:large] map[#text:http://userserve-ak.last.fm/serve/300x300/88137413.png size:extralarge]] @attr:map[rank:1] duration:369 mbid: streamable:map[#text:1 fulltrack:0]] @attr:map[country:Netherlands page:1 perPage:1 totalPages:500 total:500]]]
好的,伙计们,我浪费了一些时间,因为上述解决方案都不适合我。
所以对我来说问题是如上所述在第一个结构中的示例“Toptracks”中,我将它重命名为“Tracks”以匹配第一层。其他问题是在具有属性“Attr”的结构“Toptracks_info”中,这里的问题是在响应中我们得到“@attr”并且您不能输入@,因为它是用于命名属性的无效字符,所以我必须添加 json:“@ attr”(图像数组中的“#text”也是如此,所以示例是:
主要是
type Tracks struct {
Tracks Toptracks_info
}
type Toptracks_info struct {
Track []Track_info
Attr Attr_info `json:"@attr"`
}
type Track_info struct {
Name string
Duration string
Listeners string
Mbid string
Url string
Streamable Streamable_info
Artist Artist_info
Image []Image
Attr Track_attr_info
}
type Image struct {
Text string `json:"#text"`
Size string
}
type Attr_info struct {
Country string
Page string
PerPage string
TotalPages string
Total string
}
type Streamable_info struct {
Text string
Fulltrack string
}
type Artist_info struct {
Name string
Mbid string
Url string
}
type Track_attr_info struct {
Rank string
}
url := "http://ws.audioscrobbler.com/2.0/?method=geo.gettoptracks&api_key=xxxxxxxx&format=json&country=Netherlands"
res, err := http.Get(url)
body, err := ioutil.ReadAll(res.Body)
var data Tracks
json.Unmarshal(body, &data)
if err != nil {
panic(err.Error())
}
这是我第一次使用 Go,它让我有点发疯。
type WebKeys struct { Keys []struct { X5t string X5c []string } }
。 JSON 示例:{ "keys": [{ "x5t": "foo", "x5c": "baaaar" }] }
&http.Client{Timeout: 10 * time.Second}
还是使用整个其他库/策略?json.NewDecoder(r.Body).Decode(target)
将不会为某些类型的格式错误的 JSON 返回错误!我只是浪费了几个小时试图理解为什么我总是得到一个空响应 - 结果源 JSON 在不应该出现的地方有一个额外的逗号。我建议您改用json.Unmarshal
。还有一篇关于使用json.Decoder
here 的其他潜在危险的好文章