I have a Rest-Service using HAteoas, what worked before without pageing. Now I am producing pageable Json. I did it with out-of-the box features from Spring-Hateoas. But now I am stucking consuming it and I guess it is really not well documented, if it is.
My JSON looks like follows:
{
"_embedded": {
"vertragResourceList": [
{
"identifier": 728,
"auszubildender": "Rumm",
"beruf": "Landwirt/in",
"betrieb": "Mitterbauer Johann",
"betriebsNummer": "e12d0949-67ae-4134-9dc2-fb67758b6b16",
"zustaendigeStelle": "Irgendwo",
"beginn": 529887600000,
"status": "RECENT",
"fachrichtung": null,
"schwerpunkt": "Grünland oder Ackergras",
"ende": 623113200000,
"_links": {
"self": {
"href": "http://localhost:8080/bbsng-app-rest/vertrag/728"
}
}
},
{
"identifier": 803,
"auszubildender": "Gossen",
"beruf": "Landwirt/in",
"betrieb": "Beer Johann",
"betriebsNummer": "d5a20cb9-7273-4b75-85bd-f8e7d6a843c4",
"zustaendigeStelle": "Woanders",
"beginn": 278118000000,
"status": "RECENT",
"fachrichtung": null,
"schwerpunkt": "Ackerfutterbau",
"ende": 339116400000,
"_links": {
"self": {
"href": "http://localhost:8080/bbsng-app-rest/vertrag/803"
}
}
}
]
},
"page": {
"size": 2,
"totalElements": 1000,
"totalPages": 500,
"number": 5
}
}
====
But now my list is "_embedded", so how can I consume it in most convenient way. I would prefer out-of-the-box soltions by Spring-Hateoas or similar.
My code before worked like follows (Json was not wrapped in _embedded/vertragResourceList before!!!).
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public VertragsListe findeAlleVertraege(final Integer firstDataSet, final Integer lastDataSet, final VertragDTFilter vertragsFilter,
final VertragDTSorting vertragSorting) {
final VertragsListe vertragsListe = new VertragsListe();
final String url = LinkUtils.findeVertrag(firstDataSet, lastDataSet, vertragsFilter, vertragSorting);
final ResponseEntity<List> entity = template.getForEntity(url, List.class);
if (OK.equals(entity.getStatusCode())) {
final List<LinkedHashMap> body = entity.getBody();
for (final LinkedHashMap map : body) {
vertragsListe.add(getPopulatedVertrag(vertragsListe, map));
}
}
return vertragsListe;
}
Stacktrace:
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@e89d61c; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@e89d61c; line: 1, column: 1]
=====
EDIT:
Corresponding Resourceclass looks like this (Serverside and Clientside!!!):
public class VertragPagedResources extends PagedResources<VertragResource> {
@SuppressWarnings("unchecked")
public VertragPagedResources(final Collection<VertragResource> content, final PageMetadata metadata) {
super(content, metadata, CollectionUtils.EMPTY_COLLECTION);
}
public VertragPagedResources() {
super();
}
}
On Clientside I changed now followed:
@Autowired private RestTemplate template;
@Override
public VertragPagedResources findeAlleVertraege(final Integer firstDataSet, final Integer lastDataSet, final VertragDTFilter vertragsFilter,
final VertragDTSorting vertragSorting) {
final String url = LinkUtils.findeVertrag(firstDataSet, lastDataSet, vertragsFilter, vertragSorting);
final ResponseEntity<VertragPagedResources> entity = template.getForEntity(url, VertragPagedResources.class);
if (OK.equals(entity.getStatusCode())) {
return entity.getBody();
}
return new VertragPagedResources();
}
Now I don't get any exceptions, but content is empty. The only thing what is filled correctly are the information from pageable (numberOfReturned Datasets, pageSize and so on). The content is empty List!!! When debugging and I try out the given URL in browser, then JSON looks like above mentioned.
<200 OK,PagedResource { content: [], metadata: Metadata { number: 1, total pages: 100, total elements: 1000, size: 10 }, links: [] },{Server=[Apache-Coyote/1.1], X-Application-Context=[application:custom:8080], totalNumber=[1000], Content-Type=[application/json;charset=UTF-8], Transfer-Encoding=[chunked], Date=[Wed, 28 Jan 2015 16:58:16 GMT]}>
VertragResource (Client & Server):
public class VertragResource extends IdentifierResourceSupport {
@NotNull private String auszubildender;
@NotNull private String beruf;
@NotNull private String betrieb;
@NotNull private String betriebsNummer;
@NotNull private String zustaendigeStelle;
@NotNull private Calendar beginn;
@NotNull private String status;
private String fachrichtung;
private String schwerpunkt;
private Calendar ende;
// GETTER & SETTER ....
Controller Server-Side:
@RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity<VertragPagedResources> showAll( /* PARAMS */ ) {
// FILTER ...
final VertragFilter filter = new VertragFilter();
// FILL FILTER
// SORTING ...
final VertragSorting sorting = new VertragSorting(/* BLA */)
// COMPUTE ...
final VertragResourceAssembler assembler = new VertragResourceAssembler();
final List<Vertrag> alleVertrage = service.findeAlleVertraege(/* BLA */);
final List<VertragResource> resources = assembler.toResources(alleVertrage);
//
final long totalElements = service.zaehleAlleVertraege(filter);
final long size = Math.min(displayLength, totalElements);
final long totalPages = totalElements / size;
final PageMetadata pageMetadata = new PageMetadata(displayLength, displayStart, totalElements, totalPages);
final VertragPagedResources pagedResources = new VertragPagedResources(resources, pageMetadata);
return new HttpEntity<VertragPagedResources>(pagedResources, headerTotalNumberOfData());
}
====
IdentifierResourceSupport :
public class IdentifierResourceSupport extends ResourceSupport {
private Long identifier;
public Long getIdentifier() {
return identifier;
}
public void setIdentifier(Long identifier) {
this.identifier = identifier;
}
}
=====
EDIT 2:
What I did now, I switched Spring-Boot from 1.2.1 back to 1.1.10. Now I get an exception when trying the same, it seems that Spring-Boot 1.2.1 hides the exception:
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Unrecognized field "_embedded" (class at.compax.bbsng.client.mvc.client.resource.VertragPagedResources), not marked as ignorable (3 known properties: "links", "content", "page"])
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@62532c56; line: 1, column: 15] (through reference chain: at.compax.bbsng.client.mvc.client.resource.VertragPagedResources["_embedded"]); nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "_embedded" (class at.compax.bbsng.client.mvc.client.resource.VertragPagedResources), not marked as ignorable (3 known properties: "links", "content", "page"])
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@62532c56; line: 1, column: 15] (through reference chain: at.compax.bbsng.client.mvc.client.resource.VertragPagedResources["_embedded"])
Resources.class
. You the should get the list byentity.getBody().getContent()
. – DennisedennisonPagedResources<VertragResource>
should bePagedResources<Vertrag>
. If that doesn't solve the problem the server-side code would be important, in particular the creation and population of theVertragPagedResources
instance. – DennisedennisonT
is supposed to be the entity class. – DennisedennisonIdentifierResourceSupport
is not a Spring class, I guess its your own. Does it have any Jackson annotation or is there a mixin for it? – Dennisedennison