Http.Get'ten JSON yanıtı nasıl alınır


137

JSON verilerini web'den okumaya çalışıyorum, ancak bu kod boş sonuç döndürüyor. Burada neyi yanlış yaptığımdan emin değilim.

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

Yanıtlar:


267

İdeal yol kullanmak değilioutil.ReadAll , doğrudan okuyucu üzerinde bir kod çözücü kullanmaktır. İşte bir url alan ve yanıtını bir targetyapıya deşifre eden güzel bir işlev .

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

Örnek kullanım:

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.ClientBu cevabın orijinal olarak gösterdiği gibi, üretimde varsayılan yapıyı kullanmamalısınız ! (Hangisi http.Get/ etc çağrısıdır). Bunun nedeni, varsayılan istemcide zaman aşımı ayarlanmamış olmasıdır; uzak sunucu yanıt vermezse, kötü bir gün geçireceksiniz.


5
Yapıdaki öğelerin adları için Büyük Harf kullanmanız gerekiyor gibi görünüyor, örneğin type WebKeys struct { Keys []struct { X5t string X5c []string } } , ayrıştırdığınız JSON'daki gerçek parametreler küçük harf olsa bile. JSON örneği:{ "keys": [{ "x5t": "foo", "x5c": "baaaar" }] }
Wilson,

1
@Roman, hayır. Bir hata döndürülürse, yanıt değeri sıfırdır. (Hata, geçerli bir HTTP yanıtını okuyamadığımız anlamına gelir, kapatılacak gövde yoktur!) Bunu, var olmayan bir URL'ye .Get () işaretini işaret ederek test edebilirsiniz. Tis yöntemi, net / http belgelerinde ikinci kod bloğunda gösterilmiştir .
Connor Peet

1
@NamGVU, potansiyel bir tahsisattan tasarruf sağlar ve bağlantıları yeniden kullanmak için http canlı tutma kullanımına izin verir.
Connor Peet

2
@ConnorPeet Günümü teşekkür ettin! "Üretimde varsayılan * http.Client yapısını kullanmamalısın" ile ne demek istediğini merak ediyorum. Birinin &http.Client{Timeout: 10 * time.Second}tamamen başka bir kitaplığı / stratejiyi kullanması veya kullanması gerektiğini mi söylediniz ?
Jona Rodrigues

6
Sadece başkalarına bir uyarı - json.NewDecoder(r.Body).Decode(target)olacak değil hatalı biçimlendirilmiş JSON belirli türde bir hata döndürür! Neden boş bir yanıt almaya devam ettiğimi anlamaya çalışarak birkaç saatimi boşa harcadım - JSON kaynağında olmaması gereken yerde fazladan bir virgül vardı. Onun json.Unmarshalyerine kullanmanızı öneririm . Burada kullanmanın diğer potansiyel tehlikeleri hakkında da iyi bir json.Decoder
yazı

26

Sizin Probleminiz, verilerinizdeki dilim bildirimleriydi structs( Trackdilimler olmamaları dışında ...). Bu, structtags aracılığıyla düzeltilebilen, getirilen json dosyasındaki oldukça saçma alan adları ile birleştirildi, bkz. Godoc .

Aşağıdaki kod json'u başarıyla ayrıştırdı. Başka sorunuz varsa bana bildirin.

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

1
Yanıt gövdesinde bir şey var.
peterSO

6
Benim durumumda, "struct" alanlarında BÜYÜK HARF birinci karakteri eksikti.
2014

Aşağıdaki cevap doğrudur, doğrudan yanıtta bir Dekoder kullanmak, vücut gereksiz tahsisleri önler ve genellikle daha ideomatiktir. Cevabımı düzelttim, işaret ettiğiniz için teşekkürler.
tike

@abourget omg bu yorum için teşekkür ederim. Ayrıştırıcıdaki sorunları aramak için 1 saat harcayın ve yanıtın doğru olduğunu wireshark ile
onaylayın

14

Json paketleri tarafından kullanılmak için yapılarınızda büyük harf özellik adlarına ihtiyacınız var.

Büyük harf özellik isimleri exported properties. Küçük harfli özellik adları dışa aktarılmaz.

Ayrıca veri nesnenizi referansla ( &data) iletmeniz gerekir .

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

hala çalışmıyor, bu sizin için çalışıyor mu? aynı boş yanıt
Akshaydeep Giri

3
"json paketleri tarafından kullanılmak için yapılarınızda büyük harf özellik adlarına ihtiyacınız var." için teşekkürler.
HVNSweeting

8

json.Unmarshal(İçine var data interface{}) kaynaklı sonuçlar, Go türünüz ve değişken bildirimlerinizle doğrudan eşleşmez. Örneğin,

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

Çıktı:

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]]]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.