Jackson ist eine OpenSource Bibliothek zur Umwandlung von Java Objekten in JSON-Strings und von JSON-Strings in Java Objekte. Jackson kann für diesen Zweck entweder individuell im eigenen Projekt eingebunden werden oder wird, wie im Fall vom JBoss Wildfly, als Standard Implementierung für die Serialisierung z.B. bei Webservices verwendet.
siehe auch: Jackson mit Glassfish 4.1.1
Technisch gesehen ist jedes Java Objekt mit Jackson serialisierbar. Jackson wird ohne weiteres Eingreifen versuchen ein bestehendes Java Objekt anhand seiner öffentlichen Getter in eine JSON-Repräsentation umwandeln.
Ähnlich wie JPA kann in diese Generierung mittels Annotation eingegriffen werden um das Ergebnis nach der Serialisierung ( / Deserialisierung ) zu verändern. Zum Beispiel sind solche Modifikationen nötig wenn es darum geht sich an bestehende Formate an zu passen. Es existieren aber diverse Stellen wo es technisch notwendig ist Jackson mit zu teilen wie er mit bestimmten Situationen umgehen soll. So kann Jackson z.B. eine Bidirektionale Verbindung (Kunde Referenziert Ansprechpartner und Ansprechpartner referenziert Kunde) nicht automatisch Auflösung und würde in einer Endlosschleife enden.
Lass mich mal ran, Annotationen
(wenn nicht anders angegeben werden die Annotationen wahlweise auf dem Attribut oder dem Getter verwendet. Für eine vollständige Liste hilft die Dokumentation)
Struktur-Anpassung:
@JsonProperty(“id”), Attribut im JSON einen anderen Namen geben
@JsonProperty(index = 0), Attribut im JSON an eine andere Stelle verschieben
@JsonFormat(pattern = “dd.MM.yyyy”), Format (für Datumsfelder)
@JsonUnwrapped(prefix = “u_”), Anstatt das Referenzierte Objekt als untergeordnete Struktur zu übernehmen werden alle Attribute dieses Objektes mit einem Prefix versehen und direkt angehängt
Typen/Attribute ignorieren
@JsonIgnore, ignoriert das Attribut
@JsonIgnoreProperties({“contactName”, “fax”}), Klassenebene, ignoriert auf Basis Attributs Name
@IgnoreType(true), Klassenebene, ignoriert alle Referenzen auf diese Klasse
@JsonInclude(JsonInclude.Include.NON_EMPTY), ignoriert Attribute auf Basis von Inhalten (hier würden z.B. alle Null/Leeren Attribute nicht serialisiert werden, Default: ALL)
Perspektivenwechsel, JsonViews
Eine immer wiederkehrende Aufgabe bei Schnittstellen sind DTOs. Die Anwendung benötigt nur einen sehr kleinen Teil der Attribute von großen Objekten oder soll nicht Zugriff auf alle Attribute erhalten. Anstatt für solche Fälle ein eigenes DTO zu implementieren und die Werte zu kopieren bietet Jackson die so genannten JSONViews. Hierzu führen wir JSONView-Klassen ein die ausschließlich zur Markierung von Attribut-Subsets da sind:
public class ListView { } public class DetailView extends ListView { }
Die @JacksonView-Annotation zusammen mit diesen Klassen können wir nun innerhalb unserer Entitäten verwenden um die Attribute zu deklarieren, welche wir in einer bestimmten View vorfinden wollen. Schön ist die Möglichkeit (wie oben zu sehen) zwischen den View-Klassen Vererbung zu verwenden. Bei der Ausgabe einer DetailView würden in diesem Fall nicht nur alle Attribute ohne View-Deklaration (default) und der DetailView-Annotation zum Zuge kommen, sondern dank Vererbung auch die Attribute mit einer ListView-Annotation.
Sich im Kreis drehen
Noch einmal kurz zurück zum eingangs erwähnten Problem der sich gegenseitig referenzierenden Objekte (oder cyclic object graphs). Wir haben schon einige Annotationen gesehen mit denen wir punktuell dieses Problem lösen könnten JSON Views und JSONIgnore. Jackson bietet seit seiner Version 2 allerdings noch eine weitere Variante: @JsonIdentityInfo. Stellen wir uns eine typische Bestellung vor die mehrere Positionen enthalten. Eine Bestellung referenziert eine Liste von Positionen, die wiederrum die Bestellung referenzieren. Jackson würde sich in diesem Fall ohne Hilfe im Kreis drehen (Bestellung > Position > Bestellung > Position…. ). Mit der Annotation “IdentityInfo” auf Klasseneben können wir Jackson nun mitteilen das identische Objektreferenzen nicht erneut serialisiert werden sollen und stattdessen mit einer entsprechenden “ID Information” versehen werden sollen. Hier ein Beispiel.
@JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class, property = "id" ) public class OrderDetail {…}
Anstatt bei der erneuten Auflösung der Orderdetails im Kreis zu laufen verwendet Jackson in diesem Fall nur die ID und unterbricht den Kreis. Der Nachteil dieses Vorgehens ist natürlich offensichtlich: eine JavaScript Anwendung z.B. die ein solches JSON verarbeiten soll muss mit derlei Konstellationen umgehen können.
Das reicht doch nicht
Neben den hier gezeigten Möglichkeiten zum Eingriff in die Verarbeitung mittels Jackson über einfache Annotationen gibt es noch individuellere Möglichkeiten. Neben Factory Methoden ( @JsonCreator) in denen manuell Werte gesetzt werden können, besteht auch die Möglichkeit für Typen ganz eigene Serialisierer / Deserialiserer zu schreiben. Ein entsprechendes Beispiel gibt findet Ihr im Begleitprojekt unter: