Example code below.
Using Spring Boot 2.2, I want to communicatie with a REST API. the API I'm trying to consume wraps objects in a parent model for paging and sorting and puts an json array of the actual objects in the results
field. How would I model my Java code so jackson 'knows' how to deserialize the API responses into my java objects?
I've tried solving this using an generic in ApiResponse, and passing the expected field-type when performing the get request:
String URL_GET_DOGS = "https://localhost/api/v1/dogs/"
ApiResponse<Dog> response = this.restTemplate.getForObject(URL_GET_DOGS, response.getClass());
This compiles AND runs...
Expected result: Successfully created an ApiResponse object with a results field consisting out of a List of Dogs.
Actual result: Successfully created an ApiResponse object but the results field is a List of Objects.
So jackson won't cast the results list properly and instead I appear to get a List<Object>
instead of List<Dog>
for my results
field in my ApiResponse Object. This way I end up with properties of the wrong Type or properties that I don't want to deserialize at all! See Car example.
Now I'm back to an interface-based solution but I'm stuck. Jackson (rightfully, because there is no way to deduce the correct class...) complains that it does not know how to deserialize abstract types and I would need to provide a concrete implementation, I can't use class-type Jackson annotations as described here because I do not control the API generating the responses.
The only way out of this I see right now, is to use classes for each type of response but that means a lot of duplicate code for paging and sorting fields. What am I doing wrong?
Example JSON:
{
"count": 84,
"next": "http://localhost:80/api/v1/dogs/?limit=2&offset=2",
"previous": null,
"results": [
{
"name": "Pebbles"
},
{
"name": "Spot"
}
]
}
and another endpoint:
{
"count": 22,
"next": "http://localhost:80/api/v1/cars/?limit=2&offset=2",
"previous": null,
"results": [
{
"brand": "Mercedes",
"horse_power": 120,
"field_i_dont": "want_to_deserialize"
},
{
"brand": "BMW",
"horse_power": 180,
"field_i_dont": "want_to_deserialize"
}
]
}
Example code:
public class ApiResponse<T>{
// paging and sorting
private Long count;
private String next;
private String previous;
// the actual objects
private List<T> results;
// No-args constructor, getters & setters
}
public class Dog {
private String name;
// No-args constructor, getters & setters
}
@JsonIgnoreProperties(ignoreUnknown = true)
public class Car {
private int horsePower;
private String brand;
// No-args constructor, getters & setters
}