如何从 http 获得 JSON 响应

我尝试从 web 上读取 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()
}
291806 次浏览

您需要在结构中使用大写的属性名,以便被 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()
}

您的问题是数据 structs中的片声明(除了 Track,它们不应该是片...)。这与所获取的 json 文件中的一些相当愚蠢的字段名有关,这些字段名可以通过 structtag 修复,请参阅 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.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]]]

理想的方法是 没有使用 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 调用的)。原因是缺省客户端没有设置超时; 如果远程服务器没有响应,那么您的日子将不好过。

好了,伙计们,我在这上面浪费了一些时间,因为以上的解决方案都不适合我。

  1. 所以对我来说问题就像上面在第一个结构中的例子“ Toptrack”中所说的那样,我将它重命名为“ Tracks”以匹配第一层。
  2. 另一个问题是“ Toptrack _ info”结构中的属性“ Attr”,这里的问题是,在响应中我们得到“@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())
}

这是我第一次玩围棋,让我有点抓狂。