In früheren Artikeln haben wir bereits einige Features von Jackson gesehen die uns das Erzeugen und Verarbeiten von JSON-Strukturen einfacher macht. So lassen sich unsere Businessobjekte mittels @JsonView gefilterte in eine JSON-Ansicht bringen oder Dank @JsonIdentityInfo und entsprechenden Resolver selbst JPA-Relationen sauber verarbeiten. Zwei weitere Möglichkeiten schauen wir uns heute an
updateValue
Wenn JPA-Relationen oder auch einzelne Felder nicht ins JSON Format übertragen werden sollen lässt sich das sehr einfach mittels @JsonIgnore oder @JsonView erledigen. Bei der Verwendung solcher Strukturen als Input für das Updaten unserer Entitäten führt das jedoch zu einem Problem: nicht vorhandene Attribute werden mit “null” vorbelegt, was in aller Regel zu nicht gewollten Änderungen unserer Businessobjekte führt. Hier kommt, ähnlich wie bei der Verwendung von DTOs, in aller Regel ein Mapping-Framework ins Spiel welches die neuen Daten aus unserem JSON dazu verwendet das bestehende Objekt zu aktualisieren. Seit der Version 2.9 können wir dies auch getrost von Jackson erledigen lassen:
@PUT @Path("{id}") public Response updateAuthor(@PathParam("id") Integer id, JsonNode authorJson){ Author dbAuthor = this.authorRepository.getAuthorById(id); dbAuthor = objectMapper.updateValue(dbAuthor, authorJson); dbAuthor= this.authorRepository.merge(dbAuthor); return Response.ok().build(); }
@JsonAppend
DTOs sind ein praktisches Vorgehen um zusätzliche Attribute oder gänzlich andere Strukturen als unsere Businessobjekte per REST zu liefern. Der Aufwand zum Schreiben und der Pflege dieser DTO-Klassen sollte allerdings nicht unterschätzt werden. Wollen wir lediglich einige einzelne zusätzliche Attribute zu den bestehenden Businessobjekt-Attribute an unser JSON anhängen hält Jackson auch hierfür eine Lösung parat:
//- Entity - ------------------------------------------ @Entity @JsonAppend( attrs = { @JsonAppend.Attr(value = "bookcount") } ) public class Author {...} //- Resource - ------------------------------------------ @GET @Path("{id}") public Response getAuthor(@PathParam("id") Integer id) { ... String response=objectMapper .writerFor(Author.class) .withView(GlobalViews.Overview.class) .withAttribute("bookcount", authorById.getBooks().size()) .writeValueAsString(authorById); return Response.ok(response).build(); } }
(im Default sind diese Append-Attribute optional, werden also nur gerendert wenn auch ein Wert zugewiesen wird. Dieses Verhalten kann durch das Setzen des Annotation-Attributes ‘include’ geändert werden