How to get JSON response from http.Get

后端 未结 4 977
别那么骄傲
别那么骄傲 2020-11-29 15:22

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\"         


        
相关标签:
4条回答
  • 2020-11-29 15:40

    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()
    }
    
    0 讨论(0)
  • 2020-11-29 15:42

    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]]]
    
    0 讨论(0)
  • 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()
    }
    
    0 讨论(0)
  • 2020-11-29 15:53

    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.

    0 讨论(0)
提交回复
热议问题