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 es sich um einen SOAP Webservice oder einen Transformer handelt, der 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 verwendet! 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 von der Download Seite des Jackson Projekts herunterladen und in den CLASSPATH der Anwendung eintragen. Für das Beispiel benötigt man die Komponenten Core, Mapper und jax-xc.

Nach diesen Vorarbeiten kann man direkt zur Tat schreiten. Zuständig für die Abbildung von Java Objekten auf JSON ist die Klasse ObjectMapper. Diese wird durch einen JaxbAnnotationIntrospector, der für die Verarbeitung der JAXB Annotationen zuständig ist, angereichert:

import org.codehaus.jackson.map.*;
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;

public class JsonMusicDB {
  
  private ObjectMapper m_mapper;

  public JsonMusicDB() {
    // Create Jackson object mapper
    m_mapper = new ObjectMapper();
    // make Jackson use JAXB annotations
    AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
    m_mapper.getDeserializationConfig().setAnnotationIntrospector(introspector);
    m_mapper.getSerializationConfig().setAnnotationIntrospector(introspector);
  }

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 Komponente jax-rs sowie die Jersey JAX-RS Implementierung.