I am trying to work with the Flickr API and I have an endpoint which gives me a response like this
{
"photos": {
"page": 1,
"pages": 500,
"perpage": 1,
"total": 500,
"photo": [
{
"id": "12345",
"owner": "12345@N01",
"secret": "12ab345xyz",
"server": "1234",
"farm": 1,
"title": "Some Title",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
},
...
]
},
"stat": "ok"
}
I have a struct that looks like this
type FlickrResp struct {
Photos struct {
Page int `json:"page"` //what page the results were retreived from
Pages int `json:"pages"` //total number of pages of results
Perpage int `json:"perpage"` //number of results per page
Total int `json:"total"` //total number of results
Photo []struct {
ID string `json:"id"` //image ID
Owner string `json:"owner"` //owner ID
Secret string `json:"secret"` //owner secret
Server string `json:"server"` //server ID
Farm int `json:"farm"` //farm ID
Title string `json:"title"` //image title
Ispublic string `json:"ispublic"` //is image publicly viewable?
Isfriend string `json:"isfriend"` //is image owner my friend?
Isfamily string `json:"isfamily"` //is image owner my family?
} `json:"photo"` //array of objects with details of photos
} `json:"photos"` //object that holds information about photoset
Stat string `json:"stat"` //status
}
and I'm trying to Unmarshal thusly
var fr FlickrResp //create blank struct of type FlickrResp to hold JSON
resp, err := ioutil.ReadAll(flickrCB.Body) //read HTTP GET response
if err != nil {
}
json.Unmarshal([]byte(resp), &fr) //serialize JSON into template
flickCB.Body comes from an http.Get
The problem is that I'm only getting the first element of the photo array. I can see that I have more than one element when I print the response object cast as a string. What am I doing wrong? I've been banging my head against Unmarshal and more complicated JSON responses for a few days now.
Answered originally in comments, but for a bit more clarification:
There was an uncaught error from json.Unmarshal
:
json: cannot unmarshal number into Go value of type string
Caused by a type mismatch between the flicker API giving ispublic
, isfriend
, and isfamily
as integers, but the struct was expecting strings. An easy fix is just to convert them to ints.
Another option for these sorts of situations is to use json.Number
, which wraps a basic string from the source json and provides a few convenience methods to convert it to a number later (or just keep it as a string). By doing say
Ispublic json.Number `json:"ispublic"` //is image publicly viewable?
Isfriend json.Number `json:"isfriend"` //is image owner my friend?
Isfamily json.Number `json:"isfamily"`
It will work fine, and the result can easily be gotten as a string via fr.Ispublic.String()
.
A more elaborate change that might be useful in this situation is to use a custom boolean type. The values in question seem to mirror booleans, but integers do not map cleanly to booleans either. A custom deserialized type can be used. Borrowing from this example elsewhere on stackoverflow:
type ConvertibleBoolean bool
func (bit ConvertibleBoolean) UnmarshalJSON(data []byte) error {
asString := string(data)
if asString == "1" || asString == "true" {
bit = true
} else if asString == "0" || asString == "false" {
bit = false
} else {
return errors.New(fmt.Sprintf("Boolean unmarshal error: invalid input %s", asString))
}
return nil
}
The above would let you declare the Is*
values as ConvertibleBoolean
, and will automatically map the 0 & 1 values you'd presumably get in those fields from Flickr to booleans.