Cara mendapatkan respons JSON dari http.Get

135

Saya mencoba membaca data JSON dari web, tetapi kode itu mengembalikan hasil yang kosong. Saya tidak yakin apa yang saya lakukan salah di sini.

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()
}
Akshaydeep Giri
sumber

Jawaban:

266

Cara yang ideal adalah tidak menggunakan ioutil.ReadAll, melainkan menggunakan decoder pada pembaca secara langsung. Berikut adalah fungsi bagus yang mendapat url dan menerjemahkan responsnya ke targetstruktur.

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

Contoh penggunaan:

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

Anda seharusnya tidak menggunakan *http.Clientstruktur default dalam produksi karena jawaban ini awalnya ditunjukkan! (Yang merupakan http.Getpanggilan / etc ke). Alasannya adalah bahwa klien default tidak memiliki batas waktu yang ditetapkan; jika server jauh tidak responsif, Anda akan mengalami hari yang buruk.

Connor Peet
sumber
5
Sepertinya Anda perlu menggunakan Huruf Besar untuk nama-nama item dalam struct misalnya type WebKeys struct { Keys []struct { X5t string X5c []string } } bahkan ketika params sebenarnya di JSON yang Anda parsing berada dalam huruf kecil. Contoh JSON:{ "keys": [{ "x5t": "foo", "x5c": "baaaar" }] }
Wilson
1
@Roman, tidak. Jika kesalahan dikembalikan, nilai responsnya nol. (Kesalahan berarti kami tidak dapat membaca respons HTTP yang valid, tidak ada badan untuk ditutup!) Anda dapat menguji ini dengan menunjuk .Dapatkan () di URL yang tidak ada. Metode ini ditunjukkan dalam blok kode kedua di net / http docs.
Connor Peet
1
@NamGVU menyimpan alokasi potensial dan memungkinkan penggunaan http keep-live untuk menggunakan kembali koneksi.
Connor Peet
2
@ConnorPeet Anda membuat hari saya terima kasih! Saya ingin tahu apa yang Anda maksud dengan "Anda seharusnya tidak menggunakan struktur default * http.Client dalam produksi". Apakah maksud Anda seseorang harus menggunakan &http.Client{Timeout: 10 * time.Second}atau menggunakan seluruh perpustakaan / strategi lain?
Jona Rodrigues
6
Hanya peringatan kepada orang lain - tidakjson.NewDecoder(r.Body).Decode(target) akan mengembalikan kesalahan untuk jenis JSON cacat tertentu! Saya hanya membuang-buang waktu beberapa jam untuk mencoba memahami mengapa saya terus mendapatkan jawaban kosong - ternyata sumber JSON memiliki koma tambahan di mana seharusnya tidak. Saya sarankan Anda menggunakan sebagai gantinya. Ada juga artikel yang bagus tentang bahaya potensial lain dari penggunaan di sinijson.Unmarshaljson.Decoder
adamc
25

Masalah Anda adalah deklarasi irisan dalam data Anda structs(kecuali Track, itu tidak boleh berupa irisan ...). Ini diperparah oleh beberapa nama field yang agak konyol di file json yang diambil, yang dapat diperbaiki melalui structtags, lihat godoc .

Kode di bawah berhasil mem-parsing json. Jika Anda memiliki pertanyaan lebih lanjut, beri tahu saya.

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()
}
anak nakal
sumber
1
Ada sesuatu di badan respons.
peterSO
6
Dalam kasus saya, saya kehilangan karakter pertama UPPER-CASE di bidang "struct".
abourget
Jawaban di bawah ini benar, menggunakan Decoder langsung pada respons. Tubuh menghindari alokasi yang tidak perlu dan umumnya lebih ideomatik. Mengoreksi jawaban saya, terima kasih telah menunjukkannya.
tike
@abourget omg terima kasih atas komentar ini. Hanya menghabiskan 1 jam mencari masalah di parser, mengonfirmasi dengan wireshark bahwa responsnya benar ... terima kasih
agilob
14

Anda perlu nama properti huruf besar dalam struct Anda agar dapat digunakan oleh paket json.

Nama properti huruf besar adalah exported properties. Nama properti huruf kecil tidak diekspor.

Anda juga harus melewati objek data Anda dengan referensi ( &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()
}
Daniel
sumber
masih tidak berfungsi, apakah ini bekerja untuk Anda? same empty response
Akshaydeep Giri
3
terima kasih untuk "Anda perlu nama properti huruf besar di struct Anda agar dapat digunakan oleh paket json."
HVNTweeting
8

Hasil dari json.Unmarshal(ke var data interface{}) tidak secara langsung cocok dengan jenis Go Anda dan deklarasi variabel. Sebagai contoh,

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

Keluaran:

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