Reputation: 13241
I am trying to interact with a JSON API. It has two endpoints:
GetTravelTimeAsJSON - specify a traveltime ID and it returns a single traveltime GetTravelTimesAsJSON - returns an array with all the above TravelTimes.
So I have a struct like so:
type TravelTime struct {
AverageTime int `json:"AverageTime"`
CurrentTime int `json:"CurrentTime"`
Description string `json:"Description"`
Distance float64 `json:"Distance"`
EndPoint struct {
Description string `json:"Description"`
Direction string `json:"Direction"`
Latitude float64 `json:"Latitude"`
Longitude float64 `json:"Longitude"`
MilePost float64 `json:"MilePost"`
RoadName string `json:"RoadName"`
} `json:"EndPoint"`
Name string `json:"Name"`
StartPoint struct {
Description string `json:"Description"`
Direction string `json:"Direction"`
Latitude float64 `json:"Latitude"`
Longitude float64 `json:"Longitude"`
MilePost float64 `json:"MilePost"`
RoadName string `json:"RoadName"`
} `json:"StartPoint"`
TimeUpdated string `json:"TimeUpdated"`
TravelTimeID int `json:"TravelTimeID"`
}
If I call the API like so for a single travel time, I get a populated struct (I'm using this req lib)
header := req.Header{
"Accept": "application/json",
"Accept-Encoding": "gzip",
}
r, _ := req.Get("http://www.wsdot.com/Traffic/api/TravelTimes/TravelTimesREST.svc/GetTravelTimeAsJson?AccessCode=<redacted>&TravelTimeID=403", header)
var foo TravelTime
r.ToJSON(&foo)
dump.Dump(foo)
If I dump the response, it looks like this:
TravelTime {
AverageTime: 14 (int),
CurrentTime: 14 (int),
Description: "SB I-5 Pierce King County Line To SR 512",
Distance: 12.06 (float64),
EndPoint: {
Description: "I-5 @ SR 512 in Lakewood",
Direction: "S",
Latitude: 47.16158351 (float64),
Longitude: -122.481133 (float64),
MilePost: 127.35 (float64),
RoadName: "I-5"
},
Name: "SB I-5, PKCL To SR 512",
StartPoint: {
Description: "I-5 @ Pierce King County Line",
Direction: "S",
Latitude: 47.255624 (float64),
Longitude: -122.33113 (float64),
MilePost: 139.41 (float64),
RoadName: "I-5"
},
TimeUpdated: "/Date(1532707200000-0700)/",
TravelTimeID: 403 (int)
}
Now, what I want to do is have a struct for ALL responses, which is a slice of the TravelTime
struct, so I did this:
type TravelTimesResponse struct {
TravelTime []TravelTime
}
However, when I call the GetTravelTimesAsJSON
endpoint, and change it so:
var foo TravelTimesResponse
I get back 180 (the number of results) empty sets like this:
{
TravelTime: TravelTime {
AverageTime: 0 (int),
CurrentTime: 0 (int),
Description: "",
Distance: 0 (float64),
EndPoint: {
Description: "",
Direction: "",
Latitude: 0 (float64),
Longitude: 0 (float64),
MilePost: 0 (float64),
RoadName: ""
},
Name: "",
StartPoint: {
Description: "",
Direction: "",
Latitude: 0 (float64),
Longitude: 0 (float64),
MilePost: 0 (float64),
RoadName: ""
},
TimeUpdated: "",
TravelTimeID: 0 (int)
}
The JSON is here: https://gist.github.com/jaxxstorm/0ab818b300f65cf3a46cc01dbc35bf60
It works if I modify the original TravelTime
struct to be a slice like so:
type TravelTimes []struct {
}
but then it doesn't work as a single response.
I've managed to do this before, but for some reason the reason this failing is failing my brain. Any help appreciated.
Upvotes: 1
Views: 206
Reputation: 12675
Change the struct TravelTimesResponse
field name TravelTime
which is similar to the struct
name TravelTime
and then try to unmarshal the JSON.
type TravelTimesResponse struct {
TravelTime []TravelTime
}
Above should have different field name as:
type TravelTimesResponse struct {
TravelTimeData []TravelTime
}
[Edited]
The library is using interface{}
directly to unmarshal the response
// ToJSON convert json response body to struct or map
func (r *Resp) ToJSON(v interface{}) error {
data, err := r.ToBytes()
if err != nil {
return err
}
return json.Unmarshal(data, v)
}
Given JSON is an array of objects. And you are parsing it to struct with field name which will be an object key. Either create a slice of struct or implement unmarshal.
Unmarshal stores one of these in the interface value:
bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null
Better is to use Go package for encoding/json
.
Try Working Code on Go playground
Upvotes: -1
Reputation: 46393
The unmarshal isn't working because it's expecting the top level to be an object with a field TravelTime
, when the top level is actually the array of objects. You just want to unmarshal directly into a slice of objects, like so:
var foo []TravelTime
r.ToJSON(&foo)
Upvotes: 2