1

I'm trying a rather simple example of trying to decode a JSON file into some structs using golang. However, when attempting to I get the error that

json: cannot unmarshal object into Go value of type []main.Track

Which I don't understand. Here's the code in question, and the JSON can be found here.

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "os"
)

// Tracks a group of Track types
type Tracks struct {
    Tracks []Track
}

// Track represents a singular track
type Track struct {
    Name   string
    URL    string
    Artist Artist
}

// Artist represents a single artist
type Artist struct {
    Name string
    MBID string
    URL  string
}

func main() {
    url := "http://ws.audioscrobbler.com/2.0/?method=geo.gettoptracks&api_key=c1572082105bd40d247836b5c1819623&format=json&country=Netherlands"
    response, err := http.Get(url)

    if err != nil {
        fmt.Printf("%s\n", err)
        os.Exit(1)
    }

    defer response.Body.Close()

    var tracks Tracks
    decodeErr := json.NewDecoder(response.Body).Decode(&tracks)

    if decodeErr != nil {
        fmt.Printf("%s\n", decodeErr)
        os.Exit(1)
    }

    for _, track := range tracks.Tracks {
        fmt.Printf("%s: %s\n", track.Artist.Name, track.Name)
    }
}

My initial thought was maybe you need to categorize every single JSON value with structs, which I didn't do, but upon reading this article which says:

There are two answers to this. The easy option, for when you know what your data will look like, is to parse the JSON into a struct you’ve defined. Any field which doesn’t fit in the struct will just be ignored. We’ll cover that option first.

Which leads me to believe that it isn't that.

What am I doing wrong in this scenario?

Doug Smith
  • 29,668
  • 57
  • 204
  • 388
  • If you want to avoid this kind of issues you can try using the [json-to-go tool](https://mholt.github.io/json-to-go/). It basically converts json to a type definition. – dragosplesca Apr 25 '16 at 11:49

2 Answers2

1

If you look at the JSON structure of the response to your query you'll see that a single object with a tracks property is returned.

{
    "tracks": {
        "track": [ {...} ]
    }
}

If you compare this to to the given structs you can see that the structure does not align. Your given Tracks struct has a field that is a slice of Tracks. If I were to cast this as JSON it would be represented as the following.

{ 
    "tracks": [ { ... } ]
}

This is the root of your error. The JSON decoder is expecting the tracks field of the root JSON object to be an array. What is returned is an object that contains the tracks property but its values is another JSON object which contains the track property which is the array of tracks. You could update your structures to reflect this schema as follows.

// A response object from the API
type Response struct {
    Tracks Tracks
}

// Tracks a group of Track types
type Tracks struct {
    Track []Track
}

// Track represents a singular track
type Track struct {
    Name   string
    URL    string
    Artist Artist
}
Ben Campbell
  • 4,298
  • 2
  • 29
  • 33
0

The structure of the Go types must match the structure in the JSON data. The application is missing a level of nesting for the bolded range of this JSON: {"tracks":{"track":[{

Here's how to update the types to match the JSON:

type Tracks struct {
    Tracks struct {
      Track []Track
    }
}

...

for _, track := range tracks.Tracks.Track {
    fmt.Printf("%s: %s\n", track.Artist.Name, track.Name)
}
Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242
  • Could you explain this a little more? I'm not sure I understand. – Doug Smith Apr 24 '16 at 01:29
  • I think I see, [this answer](http://stackoverflow.com/a/25966781/998117) details it a bit, but isn't it optional to do it this way (ie: me using two outer structs to do it should still work)? – Doug Smith Apr 24 '16 at 01:53