1

Writing my first Go Application I am learning to make a basic api call and parse the json response. I am pretty sure I am not casting my types correctly and my response is

false
  0
 0

  0
 0 false
 0

If I create a few arrays with data in them I can get that response but when I add this more complexed json response to the mix things get more confusing which leads me to be quite positive I am not casting correctly.

This is my current code after playing around and changing things in order to break stuff and try and figure things out.

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
) 
    type Payload struct {
        results Data 
}

    type Data struct {
        poster_path string
        adult bool
        overview string
        release_date string
        genre_ids int
        id int
        original_title string
        original_language string
        title string
        backdrop_path string
        popularity float64
        vote_count int
        video bool
        vote_average float64
}


type poster_path map[string]string
type adult map[string]bool
type overview map[string]string
type release_date map[string]string
type genre_ids map[string]int
type id map[string]int
type original_title map[string]string
type original_language map[string]string
type title map[string]string
type backdrop_path map[string]string
type popularity map[string]float64
type vote_count map[string]int
type video map[string]bool
type vote_average map[string]float64

func main() {
      // http://image.tmdb.org/t/p/w185
      url := "https://api.themoviedb.org/3/movie/top_rated?api_key=####APIKEYHERE######"
      res, err := http.Get(url)
      if err != nil {
        panic(err)
      }
      defer res.Body.Close()

      body, err := ioutil.ReadAll(res.Body)
      if err != nil {
        panic(err)
      }
      var p Payload

      err = json.Unmarshal(body, &p)
      if err != nil {
        panic(err)
      }

      fmt.Println(
        p.results.poster_path, "\n", p.results.adult,
        p.results.overview, "\n", p.results.release_date,
        p.results.genre_ids, "\n", p.results.id,
        p.results.original_title, "\n", p.results.original_language,
        p.results.title, "\n", p.results.backdrop_path,
        p.results.popularity, "\n", p.results.vote_count,
        p.results.video, "\n", p.results.vote_average,
        )
}

This is what the JSON response looks like,

{
    "page": 1,
    "results": [
        {
            "poster_path": "/lIv1QinFqz4dlp5U4lQ6HaiskOZ.jpg",
            "adult": false,
            "overview": "Under the direction of a ruthless instructor, a talented young drummer begins to pursue perfection at any cost, even his humanity.",
            "release_date": "2014-10-10",
            "genre_ids": [
                18,
                10402
            ],
            "id": 244786,
            "original_title": "Whiplash",
            "original_language": "en",
            "title": "Whiplash",
            "backdrop_path": "/6bbZ6XyvgfjhQwbplnUh1LSj1ky.jpg",
            "popularity": 9.685051,
            "vote_count": 1706,
            "video": false,
            "vote_average": 8.36
        }
}

A few things that stand out to me,

When I tried to cast the float I was confused about casting from float32 to float64

There is an array inside the json response which was confusing when trying to cast,

"genre_ids": [
                36,
                18,
                53,
                10752
            ], 
wuno
  • 9,547
  • 19
  • 96
  • 180
  • 1
    Possible duplicate of [Converting Go struct to JSON](http://stackoverflow.com/questions/8270816/converting-go-struct-to-json) – Ainar-G Mar 11 '16 at 07:12
  • I genuinely don't mean this in a bad way, but all your `type`'s... that's just horrible. Do you really _need_ them? I mean: what's wrong with the occasional `map[string]interface{}`? You're basically declaring types that are maps, whereas (when dealing with json) a struct is just sooo much easier – Elias Van Ootegem Mar 11 '16 at 18:23
  • Hey thank you for your feedback. I will take any criticism you may offer. This is the absolute first script I have written in Go. I watched a view tutorials and this is what I gained from them. I removed all those maps and it makes a lot more sense from Pie-o-Pah answer – wuno Mar 11 '16 at 22:27

2 Answers2

4

Is a common beginners mistake. Due to language design, the encoding/json package can only unmarshal into exported fields.

From the encoding/json package:

To unmarshal JSON into a struct, Unmarshal matches incoming object keys to the keys used by Marshal (either the struct field name or its tag), preferring an exact match but also accepting a case-insensitive match. Unmarshal will only set exported fields of the struct.

To export a field, simply use a capital first letter of the name. Eg.:

type Payload struct {
    Results Data 
}

instead of

type Payload struct {
    results Data 
}
ANisus
  • 74,460
  • 29
  • 162
  • 158
  • 1
    Hey man thank you so much. That makes sense and helped. I am almost there. Would you mind taking a look at the types vs the type in the json and make sure I am casting correctly? Because I am getting this now go run request.go panic: json: cannot unmarshal array into Go value of type main.Data – wuno Mar 11 '16 at 08:08
  • @wuno Sure. The error is simple. `"results"` is an array of data objects (as the error says), but in Go you have defined `Results` as a simple `Data` object. Just change `Results Data` to `Results []Data` – ANisus Mar 11 '16 at 09:03
  • So funny. Right before you answered me I found something talking about this. I am sure I am not casting correctly and when I make that change I still have a bunch of this now .Results.Backdrop_path undefined (type []Data has no field or method Backdrop_path) – wuno Mar 11 '16 at 09:07
  • @wuno: No. Results doesn't have any fields. Results is a slice. But `Results[0].Backdrop_path` will work .. given the JSON object contains at least one result object in the array. – ANisus Mar 11 '16 at 09:09
  • 1
    Thanks man, I really appreciate you. I can loop through the array now but its still just printing out 0 and false. for i := 0; i < 10; i++ { fmt.Println( p.Results[i].poster_path, results in false 0 0 0 0 false 0 false 0 0 0 0 fals – wuno Mar 11 '16 at 09:14
  • @wuno probably because you haven't included JSON tags to help Go to match your exported fields. See my answer it worked just fine. – Pandemonium Mar 11 '16 at 09:29
  • @wuno Exactly like pie-o-pah says: You have to export the fields in the `Data` type as well. Eg. `poster_path` -> `Poster_path` – ANisus Mar 11 '16 at 10:14
1

First of all, you are missing a closing square bracket ] at the end of results in your JSON.

Secondly, you did not structure your structs according to the JSON you receive.

Lastly, use JSON tags after each exported field in your struct when dealing with Unmarshal/marshaling to help Go detect the appropriate fields (not necessary if you name the fields according to how Unmarshal/marshal identify fields.

type Payload struct {
        Page    int
        Results []Data 
}

type Data struct {
        PosterPath       string  `json:"poster_path"`
        Adult            bool    `json:"adult"`
        Overview         string  `json:"overview"`
        ReleaseDate      string  `json:"release_date"`
        GenreIds         []int   `json:"genre_ids"`
        Id               int     `json:"id"`
        OriginalTitle    string  `json:"original_title"`
        OriginalLanguage string  `json:"original_language"`
        Title            string  `json:"title"`
        BackdropPath     string  `json:"backdrop_path"`
        Popularity       float64 `json:"popularity"`
        VoteCount        int     `json:"vote_count"`
        Video            bool    `json:"video"`
        VoteAverage      float64 `json:"vote_average"`
}

Note that GenreIds has to be []int to match the JSON data as well. And it's a good idea not to use CamelCase in Go.

See https://play.golang.org/p/VduPD9AY84

Pandemonium
  • 7,724
  • 3
  • 32
  • 51
  • 1
    Note that you do not need struct tags: you use them when the JSON key names do not match your Go struct field names. – elithrar Mar 11 '16 at 14:17
  • @elithrar thanks it's a safeguard since wuno's few original field names don't match the JSON. – Pandemonium Mar 11 '16 at 14:39
  • 1
    I know; I'm being a pedant and saying that "always use JSON tags" is not entirely correct. It's important for the OP to know when you would use them; the real problem was the unexported struct fields. – elithrar Mar 11 '16 at 14:40
  • @elithrar you are right. Edited as per your comment! – Pandemonium Mar 11 '16 at 18:21