I want to consume a paginated API. The API is provided by an endpoint, created with SpringBoot. While looking for a solution I found this post on StackOverflow.
Almost every answer recommends to create a POJO to parse into. But since this is a standard problem, there should be a standard solution which is supported by the framework. In the mentioned post there is an answer by Vladimir Mitev. He recommands the use of ParameterizedTypeReference<PagedResources<T>>
. I took his snipped and changed the Type T
for my purpose:
restTemplate.exchange(targetUrl, HttpMethod.GET, requestEntity, new ParameterizedTypeReference<PagedResources<String>>() {});
And this worked for getting the content of the Page. But the Problem here is, that the provided metadata keeps being null. Thus I am unable to iterate over the pages since I do not get the information about totalPages
. Am I defining the ResponseType correctly? Debbuging led me to clue that the default constructor of PagedResources is called, so the PageMetadata
Object is never set.
In the following I describe how I approached the problem. Maybe it helps others, that face the same problem
The naive way to consume the paginated API was:
restTemplate.exchange(targetUrl, HttpMethod.GET, requestEntity, String.class);
This gives us an idea of the underlying datascructure, which looks like this.
<200,
{
"content":[
"Data1",
"Data2",
"Data3"
],
"pageable":{
"sort":{
"sorted":false,
"unsorted":true,
"empty":true
},
"offset":0,
"pageSize":20,
"pageNumber":0,
"paged":true,
"unpaged":false
},
"number":0,
"sort":{
"sorted":false,
"unsorted":true,
"empty":true
},
"size":20,
"first":true,
"numberOfElements":20,
"totalPages":5,
"totalElements":90,
"last":false,
"empty":false
},
[
Cache-Control:"no-cache, no-store, max-age=0, must
-revalidate",
Content-Type:"application/json;charset=UTF-8",
Date:"Thu, 20 Jun 2019 17:38:18 GMT",
Expires:"0",
Pragma:"no-cache",
Server:"nginx/1.15.10",
X-Content-Type-Options:"nosniff"
It looks like a standard spring pagionation response object. So there should be a way to directly transform this response into some kind of pre defined object.
Therefore I tried changing the ResponseType parameter:
Page.class
resulting in
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.springframework.data.domain.Page]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of org.springframework.data.domain.Page (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
and PageImpl.class
resulting in
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.springframework.data.domain.PageImpl]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of org.springframework.data.domain.PageImpl (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
So there has to be another ResponseType to parse the response into.