I'm having an issue where the API I'm parsing returns an OBJECT for an ARRAY of size 1.
For example, sometimes the API will respond with:
{
"monument": [
{
"key": 4152,
"name": "MTS - Corporate Head Office",
"categories": {},
"address": {}
},
{
"key": 4151,
"name": "Canadian Transportation Agency",
"categories": {},
"address": {}
},
{
"key": 4153,
"name": "Bank of Montreal Building",
"categories": {},
"address": {}
}
],
}
However, if the monument
array has only 1 item it becomes an OBJECT (note the lack of []
brackets) like so:
{
"monument": {
"key": 4152,
"name": "MTS - Corporate Head Office",
"categories": {},
"address": {}
}
}
If I define my models like this, I will get an error when only a single item is returned:
public class Locations {
public List<Monument> monument;
}
If only a single item is returned I get the following error:
Expected BEGIN_OBJECT but was BEGIN_ARRAY ...
And if I define my model like so:
public class Locations {
public Monument monument;
}
and the API returns an ARRAY I get the opposite error
Expected BEGIN_ARRAY but was BEGIN_OBJECT ...
I cannot define multiple items with the same name in my model. How can I handle this case?
Note: I cannot make changes to the API.
The trick is to write your own Gson deserializer for your Locations
class. This would check whether the monument element is an object or an array. Like so:
public class LocationsDeserializer implements JsonDeserializer<Locations> {
@Override
public Locations deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonElement monumentElement = json.getAsJsonObject().get("monument");
if (monumentElement.isJsonArray()) {
return new Locations((Monument[]) context.deserialize(monumentElement.getAsJsonArray(), Monument[].class));
} else if (monumentElement.isJsonObject()) {
return new Locations((Monument) context.deserialize(monumentElement.getAsJsonObject(), Monument.class));
} else {
throw new JsonParseException("Unsupported type of monument element");
}
}
}
For the convenience, add a vararg constructor to your Locations
class:
public class Locations {
public List<Monument> monuments;
public Locations(Monument ... ms) {
monuments = Arrays.asList(ms);
}
}
Your Monument class stays the same. Something like:
public class Monument {
public int key;
public String name;
// public Categories categories;
// public Address address;
}
Finally, create your own Gson
object and pass it to the retrofit RestAdapter
.
Gson gson = new GsonBuilder().registerTypeAdapter(Locations.class, new LocationsDeserializer()).create();
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(baseUrl)
.setConverter(new GsonConverter(gson))
.build();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With