Cách nhận phản hồi JSON từ http.Get


135

Tôi đang cố đọc dữ liệu JSON từ web, nhưng mã đó trả về kết quả trống. Tôi không chắc những gì tôi đang làm sai ở đây.

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

Câu trả lời:


266

Cách lý tưởng là không sử dụng ioutil.ReadAll, mà là sử dụng bộ giải mã trực tiếp trên đầu đọc. Đây là một chức năng tốt có được một url và giải mã phản hồi của nó trên một targetcấu trúc.

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

Ví dụ sử dụng:

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

Bạn không nên sử dụng *http.Clientcấu trúc mặc định trong sản xuất vì câu trả lời này ban đầu được thể hiện! (Đó là những gì http.Get/ vv gọi đến). Lý do là máy khách mặc định không có thời gian chờ; nếu máy chủ từ xa không phản hồi, bạn sẽ có một ngày tồi tệ.


5
Có vẻ như bạn cần sử dụng Uppercase cho tên của các mục trong cấu trúc, ví dụ như type WebKeys struct { Keys []struct { X5t string X5c []string } } ngay cả khi các thông số thực tế trong JSON mà bạn phân tích cú pháp ở dạng chữ thường. Ví dụ JSON:{ "keys": [{ "x5t": "foo", "x5c": "baaaar" }] }
Wilson

1
@Roman, không. Nếu một lỗi được trả về, giá trị phản hồi là 0. (Lỗi có nghĩa là chúng tôi không thể đọc bất kỳ phản hồi HTTP hợp lệ nào, không có cơ quan nào để đóng!) Bạn có thể kiểm tra điều này bằng cách trỏ .Get () vào một URL không tồn tại. Phương pháp Tis được thể hiện trong khối mã thứ hai trong tài liệu net / http .
Connor Peet

1
@NamGVU lưu phân bổ tiềm năng và cho phép sử dụng http keep-life để sử dụng lại các kết nối.
Connor Peet

2
@ConnorPeet Bạn đã cảm ơn ngày của tôi! Tôi tự hỏi ý của bạn là "Bạn không nên sử dụng cấu trúc * http.Client mặc định trong sản xuất". Ý bạn là người ta nên sử dụng &http.Client{Timeout: 10 * time.Second}hay sử dụng toàn bộ thư viện / chiến lược khác?
Jona Coleues

6
Chỉ là một cảnh báo cho người khác - json.NewDecoder(r.Body).Decode(target)sẽ không trả lại lỗi cho một số loại JSON không đúng định dạng! Tôi chỉ lãng phí một vài giờ để cố gắng hiểu lý do tại sao tôi tiếp tục nhận được phản hồi trống - hóa ra JSON nguồn có thêm dấu phẩy ở nơi không nên có. Tôi đề nghị bạn sử dụng json.Unmarshalthay thế. Ngoài ra còn có một bài viết hay về những nguy cơ tiềm ẩn khác của việc sử dụng json.Decoder ở đây
adamc

25

Vấn đề của bạn là khai báo lát trong dữ liệu của bạn structs(ngoại trừ Track, chúng không nên là lát ...). Điều này được kết hợp bởi một số tên trường khá ngớ ngẩn trong tệp json được tìm nạp, có thể được sửa chữa thông qua structtags, xem godoc .

Mã dưới đây đã phân tích json thành công. Nếu bạn có thêm câu hỏi, hãy cho tôi biết.

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
Có một cái gì đó trong cơ thể phản ứng.
peterSO

6
Trong trường hợp của tôi, tôi đã thiếu ký tự đầu tiên UPPER-CASE trong các trường "struct".
abourget

Câu trả lời dưới đây là đúng, sử dụng Bộ giải mã trực tiếp trên phản hồi. Hiện tại, tránh các phân bổ không cần thiết và thường mang tính ý thức hệ cao hơn. Sửa câu trả lời của tôi, cảm ơn vì đã chỉ ra.
tike

@abourget omg cảm ơn bạn đã nhận xét này. Chỉ cần dành 1 giờ để tìm kiếm các vấn đề trong trình phân tích cú pháp, xác nhận với wireshark rằng phản hồi là chính xác ... cảm ơn
agilob

14

Bạn cần tên thuộc tính chữ hoa trong cấu trúc của bạn để được sử dụng bởi các gói json.

Tên thuộc tính trường hợp trên là exported properties. Tên tài sản viết thường không được xuất.

Bạn cũng cần truyền đối tượng dữ liệu của mình bằng tham chiếu ( &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()
}

vẫn không làm việc, điều này có làm việc cho bạn không? cùng một câu trả lời trống rỗng
Akshaydeep Giri

3
cảm ơn vì "Bạn cần tên thuộc tính chữ hoa trong cấu trúc của mình để được các gói json sử dụng."
HVNSweeting

8

Các kết quả từ json.Unmarshal(thành var data interface{}) không khớp trực tiếp với khai báo biến và loại Go của bạn. Ví dụ,

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

Đầu ra:

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]]]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.