Doug Smith
Doug Smith

Reputation: 29326

Why does Go error when trying to marshal this JSON?

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?

Upvotes: 1

Views: 2120

Answers (2)

Thundercat
Thundercat

Reputation: 121029

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

Upvotes: 0

Ben Campbell
Ben Campbell

Reputation: 4678

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
}

Upvotes: 1

Related Questions