How to get JSON response from http.Get

JsonGo

Json Problem Overview


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

Json Solutions


Solution 1 - Json

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.

Solution 2 - Json

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

Solution 3 - Json

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

Solution 4 - Json

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

Solution 5 - Json

Ok my dudes, I wasted some time on this because none of the above solutions were working for me.

  1. So what the issue for me was as stated above in the examples "Toptracks" in the first struct, I renamed it to "Tracks" to match the first layer.
  2. Other issue was in struct "Toptracks_info" with property "Attr", the problem here is that in the response we get "@attr" and you cannot put @ because it's an invalid char for naming properties so I had to add json:"@attr" (same thing goes for "#text" in image array So the example is :

and main is

type Tracks struct {
    Tracks 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
    Image      []Image
    Attr       Track_attr_info
}
type Image struct {
	Text string `json:"#text"`
	Size string
}

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
}


url := "http://ws.audioscrobbler.com/2.0/?method=geo.gettoptracks&api_key=xxxxxxxx&format=json&country=Netherlands"

res, err := http.Get(url)

body, err := ioutil.ReadAll(res.Body)
var data Tracks
json.Unmarshal(body, &data)

if err != nil {
	panic(err.Error())
}

It's my first time with Go and it drove me a bit crazy.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAkshay Deep GiriView Question on Stackoverflow
Solution 1 - JsonConnor PeetView Answer on Stackoverflow
Solution 2 - JsontikeView Answer on Stackoverflow
Solution 3 - JsonDanielView Answer on Stackoverflow
Solution 4 - JsonpeterSOView Answer on Stackoverflow
Solution 5 - JsonA2NView Answer on Stackoverflow