如何从http.Get获取JSON响应

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

How to get JSON response from http.Get

问题

我正在尝试从网络上读取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()
}
英文:

I'm trying read JSON data from web, but that code returns empty result. I'm not sure what I'm doing wrong here.

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

答案1

得分: 361

理想的方式不是使用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等调用的默认客户端)。原因是默认客户端没有设置超时;如果远程服务器无响应,你将会度过糟糕的一天。

英文:

The ideal way is not to use ioutil.ReadAll, but rather use a decoder on the reader directly. Here's a nice function that gets a url and decodes its response onto a target structure.

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

Example use:

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

You should not be using the default *http.Client structure in production as this answer originally demonstrated! (Which is what http.Get/etc call to). The reason is that the default client has no timeout set; if the remote server is unresponsive, you're going to have a bad day.

答案2

得分: 32

你的问题在于数据结构体中的切片声明(除了Track,它们不应该是切片...)。这还受到从获取的JSON文件中一些相当愚蠢的字段名的影响,可以通过结构标签进行修复,参见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()
}
英文:

Your Problem were the slice declarations in your data structs (except for Track, they shouldn't be slices...). This was compounded by some rather goofy fieldnames in the fetched json file, which can be fixed via structtags, see godoc.

The code below parsed the json successfully. If you've further questions, let me know.

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

答案3

得分: 24

你的结构体中的属性名需要大写才能被json包使用。

大写的属性名是“导出属性”。小写的属性名不会被导出。

你还需要通过引用传递你的数据对象(&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数据
    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()
}
英文:

You need upper case property names in your structs in order to be used by the json packages.

Upper case property names are exported properties. Lower case property names are not exported.

You also need to pass the your data object by reference (&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()
}

答案4

得分: 10

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
}

英文:

The results from json.Unmarshal (into var data interface{}) do not directly match your Go type and variable declarations. For example,

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

Output:

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]]]

答案5

得分: 3

Ok my dudes, I wasted some time on this because none of the above solutions were working for me.

  1. So what the issue for me was as stated above in the examples "Toptracks" in the first struct, I renamed it to "Tracks" to match the first layer.
  2. Other issue was in struct "Toptracks_info" with property "Attr", the problem here is that in the response we get "@attr" and you cannot put @ because it's an invalid char for naming properties so I had to add json:"@attr" (same thing goes for "#text" in image array
    So the example is :

and main is

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

It's my first time with Go and it drove me a bit crazy.

英文:

Ok my dudes, I wasted some time on this because none of the above solutions were working for me.

  1. So what the issue for me was as stated above in the examples "Toptracks" in the first struct, I renamed it to "Tracks" to match the first layer.
  2. Other issue was in struct "Toptracks_info" with property "Attr", the problem here is that in the response we get "@attr" and you cannot put @ because it's an invalid char for naming properties so I had to add json:"@attr" (same thing goes for "#text" in image array
    So the example is :

and main is

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

It's my first time with Go and it drove me a bit crazy.

huangapple
  • 本文由 发表于 2013年6月18日 04:35:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/17156371.html
匿名

发表评论

匿名网友

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

确定