http.Get에서 JSON 응답을 얻는 방법


135

웹에서 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()
}

답변:


266

이상적인 방법은 없는 용도 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/ etc가 호출하는 것은 무엇입니까 ). 그 이유는 기본 클라이언트에 시간 초과가 설정되어 있지 않기 때문입니다. 원격 서버가 응답하지 않으면 나쁜 하루를 보낼 것입니다.


5
type WebKeys struct { Keys []struct { X5t string X5c []string } } 구문 분석중인 JSON의 실제 매개 변수가 소문자 인 경우에도 구조체의 항목 이름에 대문자를 사용해야하는 것처럼 보입니다 . JSON 예 :{ "keys": [{ "x5t": "foo", "x5c": "baaaar" }] }
Wilson

1
@ 로마 오류가 리턴되면 응답 값은 nil입니다. (오류는 유효한 HTTP 응답을 읽을 수 없다는 것을 의미합니다. 닫을 본문이 없습니다!) 존재하지 않는 URL에서 .Get ()을 가리켜 서 테스트 할 수 있습니다. Tis 방법은 net / http 문서 의 두 번째 코드 블록에서 설명합니다 .
Connor Peet

1
@NamGVU는 잠재적 할당을 절약하고 http keep-alive를 사용하여 연결을 재사용 할 수 있도록합니다.
코너 피트

2
@ConnorPeet 당신은 내 하루 감사합니다! "프로덕션에서 기본 * http.Client 구조를 사용해서는 안된다"는 의미가 무엇인지 궁금합니다. &http.Client{Timeout: 10 * time.Second}다른 도서관 / 전략을 사용 하거나 사용해야 한다는 것을 의미 했습니까 ?
Jona Rodrigues

6
그냥 다른 사람에게 경고 - json.NewDecoder(r.Body).Decode(target)없는 잘못된 JSON 특정 유형의 오류를 반환! 나는 왜 빈 응답을 계속 받는지 이해하려고 몇 시간을 낭비했습니다. 소스 JSON에는 불필요한 쉼표가 있다는 것이 밝혀졌습니다. json.Unmarshal대신 사용 하는 것이 좋습니다 . json.Decoder 여기
adamc

25

귀하의 문제는 데이터의 슬라이스 선언이었습니다 structs(을 제외하고는 Track슬라이스가 아니어야합니다 ...). 이것은 참조 structtags 통해 고정 될 수 페치 JSON 파일 다소 구피 fieldName에 의해 혼합 하였다 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()
}

1
응답 본문에 무언가가 있습니다.
peterSO

6
제 경우에는 "struct"필드에 대문자 대문자 첫 문자가 없습니다.
abourget

응답에 직접 디코더를 사용하여 아래 답변이 옳습니다. 바디는 불필요한 할당을 피하고 일반적으로 더 관념적입니다. 지적 해 주셔서 감사합니다.
tike

@abourget omg이 의견에 감사드립니다. 구문 분석기에서 문제를 찾는 데 1 시간을 투자하고 wireshark를 통해 응답이 올바른지 확인하십시오 ... 감사합니다
agilob

14

json 패키지에서 사용하려면 구조체에 대문자 속성 이름이 필요합니다.

대문자 속성 이름은 exported properties입니다. 소문자 속성 이름은 내 보내지 않습니다.

또한 데이터 객체를 참조 ( &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()
}

여전히 작동하지 않습니다, 이것은 당신을 위해 작동합니까? 같은 빈 응답
Akshaydeep Giri 2016

3
"json 패키지에서 사용하려면 구조체에 대문자 속성 이름이 필요합니다."
HVNSweeting

8

의 결과 json.Unmarshal(로는 var data interface{}) 직접 이동 유형 및 변수 선언과 일치하지 않습니다. 예를 들어

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

산출:

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]]]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.