JAXB und Jackson: Speichern von Java Objekten als JSON

Martin Kompf

Der Jackson JSON Processor und die Java Architecture for XML Binding (JAXB) bieten ein einfach anzuwendendes Werkzeug zur Serialisierung und Deserialisierung von Java Objekten nach und von JSON. Die Abbildung von Java Objekten auf JSON Daten erfolgt dabei mittels der bekannten JAXB Annotationen.

JSON oder XML?

Im Artikel JAXB: Speichern von Java Objekten als XML wurden JAXB Annotation verwendet, um die Abbildung von Java Objekten auf XML Daten zu beschreiben. Die annotierten Klassen des Datenmodells konnten dann unter Zuhilfenahme der JAXB API mittels einfacher Anweisungen in einen XML Datenstrom umgewandelt werden.

Die Verwendung von XML ist ideal, wenn der Empfänger der Daten optimal darauf eingestellt ist, zum Beispiel wenn ein SOAP Webservice die Daten konsumiert oder ein Transformer das XML nach HTML oder PDF konvertiert. Handelt es sich bei der Gegenstelle dagegen um eine in JavaScript oder PHP geschriebene Webanwendung, so wird man bald feststellen, das XML hier nicht die richtige Wahl für das Datenformat war. PHP und JavaScript haben keine eingebaute JAXB API und damit auch keine Möglichkeit, das empfangene XML direkt in Objekte umzuwandeln.

In diesem Anwendungsbereich hat sich in den letzten Jahren vielmehr die JavaScript Object Notation - kurz JSON - als universelles Datenaustauschformat durchgesetzt. Die meisten im Webumfeld verwendeten Programmiersprachen haben mittlerweile eingebaute Methoden, die aus einem ankommenden JSON Datenstrom direkt ein Objekt erzeugen beziehungsweise umgekehrt Objekte nach JSON serialisieren. Zum Beispiel bietet PHP hierfür die beiden Funktionen json_decode und json_encode.

Java + JSON = ?

Als Java Entwickler steht man daher oft vor der Aufgabe, Daten im JSON Format lesen oder schreiben zu müssen. Das JSON Projekt bietet dafür eine Low-Level Java API an, die auch Bestandteil vieler bekannter Produkte, wie zum Beispiel Android ist. Diese API bildet im Prinzip alle JSON Daten auf die zwei Java Klassen JSONArray und JSONObjekt ab:

// create JSON
JSONObject o = new JSONObject();
o.put("name", "Horst");
o.put("code", 7);
String json = o.toString();
 
System.out.println(json);

// parse JSON
JSONObject p = new JSONObject(json);
String name = p.getString("name");
int code = p.getInt("code");
System.out.printf("%s hat Code %03d%n", name, code);

Dieses Vorgehen bietet zwar einen schnellen Einstieg und mag für kleine Projekte ausreichend sein. Aber schon bei der Aufgabe, das relativ simple Datenmodell aus JAXB: Speichern von Java Objekten als XML nach JSON zu serialisieren, zeigen sich die Nachteile dieser API: Man muss zuerst alle Objekte des domänenspezifischen Datenmodells in ein JSONObject umbauen, damit man dieses dann serialisieren kann. Umgekehrt entstehen bei der Deserialisierung zunächst JSONObject und JSONArray - doch wie bekommt man daraus dann Objekte des Typs MyMusicCollection, Album und Title?

Java + JSON = Jackson!

Eine Lösung hierfür ist der Jackson JSON Processor. Er ermöglicht - analog zu JAXB - eine direkte Umwandlung von Java Objekten nach JSON und umgekehrt. Und das Beste daran ist, dass Jackson die gleichen Annotationen wie JAXB verwenden kann! Damit können die in JAXB: Speichern von Java Objekten als XML erarbeiteten Annotationen des Datenmodells direkt wiederverwendet werden! Mehr noch, soll das Programm sowohl XML als auch JSON erzeugen, dann muss am Datenmodell überhaupt nichts geändert werden. Man ruft lediglich zur Laufzeit Jackson anstelle von JAXB auf.

Jackson ist nicht Bestandteil der Standard Java API. Man muss daher die entsprechenden JAR Files separat herunterladen und in den CLASSPATH der Anwendung eintragen. Für das Beispiel benötigt man die drei Kernkomponenten Streaming, Databind und Annotations sowie das Modul JAXB Annotations.

Ein bequemer Weg zur Installation der erforderlichen Jackson-Komponenten ist die Verwendung von Apache Ivy. Falls noch nicht geschehen, installiert man es zunächst per

sudo apt-get install ivy

Dann erzeugt man eine Datei ivy.xml mit dem Inhalt:

<ivy-module version="2.0">
  <info organisation="de.kompf" module="javaxml" />
  <configurations>
    <conf name="default" visibility="public" />
  </configurations>
  <dependencies>
    <dependency org="com.fasterxml.jackson.module" 
      name="jackson-module-jaxb-annotations"
      rev="2.6.3" conf="default" />
  </dependencies>
</ivy-module> 

Der wesentliche Inhalt von ivy.xml ist die Definition der Abhängigkeit zu jackson-module-jaxb-annotations - im Beispiel mit der Version 2.6.3. Die zu diesem Modul gehörenden Artefakte inklusive aller Abhängigkeiten lädt dann der ivy Task retrieve auf die Platte. Bei größeren Projekten integriert man den Aufruf von ivy:retrieve sinnvollerweise in sein Ant-Buildfile. Für den Anfang tut es aber auch folgender Einzeiler für die Shell:

java -jar /usr/share/java/ivy.jar -ivy ivy.xml -retrieve [artifact].[ext]

Nach diesen Vorarbeiten kann man direkt zur Tat schreiten. Zuständig für die Abbildung von Java Objekten auf JSON ist die Klasse ObjectMapper. Die Registierung des Moduls JaxbAnnotationModule versetzt ihn in die Lage, auch mit JAXB Annotationen umgehen zu können:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;

public class JsonMusicDB {
  
  private ObjectMapper m_mapper;

  public JsonMusicDB() {
    // Create Jackson object mapper
    m_mapper = new ObjectMapper();
    // make Jackson use JAXB annotations
    JaxbAnnotationModule module = new JaxbAnnotationModule();
    m_mapper.registerModule(module);
  }

Die Methoden zum Serialisieren und Deserialisieren eines Java Objektbaums sind an Einfachheit kaum zu übertreffen:

  private void writeMusic(MyMusicCollection music, File file) throws IOException {
    m_mapper.writeValue(file, music);
  }
  
  private MyMusicCollection readMusic(File file) throws IOException {
    return m_mapper.readValue(file, MyMusicCollection.class);
  }

Die Klasse MyMusicCollection stammt aus dem bereits erwähnten Artikel JAXB: Speichern von Java Objekten als XML, aus dem auch der restliche Beispielcode übernommen werden kann.

Fazit

Das Beispiel zeigt, wie einfach sich die Umwandlung eines Java Datenmodells in eine JSON Repräsentation mittels Jackson gestaltet. Als Benefit können dabei JAXB Annotationen wiederverwendet werden, um ohne Konfigurationsdateien die Art und Weise der Datenbindung zu beeinflussen. Damit ist auf elegante Art und Weise ein Brückenschlag zur Welt der Webanwendungen möglich, die mit JavaScript, PHP und anderen Sprachen eine JSON API eingebaut haben. Auch die im Internet weit verbreiteten REST Services benutzen als Datenformat oftmals JSON. Hier lohnt sich dann ein zusätzlicher Blick auf die Jackson Provider für JAX-RS.