Consuming Spring Hateoas Pageable

Ich habe einen Rest-Service mit HAteoas, was vorher ohne Pagen geklappt hat. Jetzt produziere ich seitenweise Json. Ich habe es mit Out-of-the-Box-Features von Spring-Hateoas gemacht. Aber jetzt stecke ich fest, es zu konsumieren, und ich denke, es ist wirklich nicht gut dokumentiert, wenn es so ist.

Mein JSON sieht wie folgt aus:

{
"_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
}
}

====

Aber jetzt ist meine Liste "_embedded", also wie kann ich es am bequemsten konsumieren. Ich würde Standardlösungen von Spring-Hateoas oder ähnlichem bevorzugen.

Mein Code hat vorher wie folgt funktioniert Json war vorher nicht in _embedded / vertragResourceList eingeschlossen !!!).

@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]

=====

BEARBEITEN

Entsprechende Ressourcenklasse sieht so aus (Serverseite und Clientseite !!!):

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();
    }

}

uf Kundenseite habe ich jetzt folgendes geändert:

@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();
}

Jetzt bekomme ich keine Ausnahmen, aber der Inhalt ist leer. Das einzige, was richtig ausgefüllt ist, sind die Informationen aus seitenweiser (numberOfReturned Datasets, pageSize usw.). Der Inhalt ist leer Liste !!! Wenn ich beim Debuggen die angegebene URL im Browser ausprobiere, sieht JSON wie oben beschrieben aus.

<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-Seite:

@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:

Was ich jetzt getan habe, habe ich Spring-Boot von 1.2.1 auf 1.1.10 zurückgesetzt. Jetzt bekomme ich eine Ausnahme, wenn ich das Gleiche versuche. Es scheint, dass Spring-Boot 1.2.1 die Ausnahme verbirgt:

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"])

Antworten auf die Frage(2)

Ihre Antwort auf die Frage