Donnerstag, 4. Dezember 2008





April 2006
aus Java Magazin Ausgabe: 10.2001
XML leicht gemacht
Die Java Architecture for XML Binding (JAXB) in der Praxis
Andreas Holubek, Frank Ulbricht

Die Nutzung von XML-Dokumenten für die verschiedensten Aufgabengebiete gehört inzwischen zum festen Standard unter Java. Aufgrund der vielen Vorteile eines solchen Datenformates wurden einige Nachteile bei der Entwicklung in Kauf genommen. So wird vor allem das Lesen, Verändern und Schreiben immer wieder zu einem zeitaufwendigen Programmier-Problem. Die Java Architecture for XML Binding (JAXB) soll hier eine Lösung bieten.


Architektur
Als erstes fällt sicherlich der integrierte Parser auf. Für die Nutzung von JAXB wird kein externer Parser benötigt, wie er zum Beispiel durch das JAXP Framework zur Verfügung gestellt wird. Der Grund dafür ist leicht zu finden. Die generierten Klassen wissen um die Zusammensetzung des Datenstroms. Mit diesen Informationen ist der Pars-Prozess schneller als die SAX Variante. Durch den bekannten Aufbau der Objekte wiederum wird viel Overhead aus der DOM Variante einfach weggelassen. Zusammengefasst erzeugt der im JAXB enthaltene Compiler speziell angepassten, sehr schnellen Quelltext, um ganz spezielle XML-Dateien zu Lesen und zu Schreiben. Ausgangspunkt der Entwicklung ist der Document Type Descriptor (DTD). Aus diesem wird mittels eines durch JAXB bereitgestellten Compilers (XJC) eine Menge von abhängigen Klassen und Hilfsmethoden erzeugt. Mit Hilfe der generierten Klassen ist wiederum die Wandlung von und nach XML möglich. Hierbei wird der Übergang vom XML-Dokument zum Objektbaum als unmarshalling und der Übergang von den Objekten zu XML als marshalling bezeichnet. Abbildung 1 verdeutlicht die prinzipiellen Zusammenhänge.


Abb.1: Zusammenhänge in der JAXB Architektur

Die primären Komponenten der Architektur lassen sich wie folgt beschreiben:
  • Schema Compiler: Der Schema Compiler transformiert oder bindet ein Ausgangsschema an eine Menge von abhängigen Klassen. Innerhalb von JAXB EA1 wird hierzu die DTD von XML 1.0 genutzt.

  • Binding Framework: Stellt ein Framework von öffentlichen Klassen und Interfaces dar, auf welchem die vom Schema Compiler erzeugten Klassen aufbauen. Die vom Binding Framework bereitgestellten Klassen implementieren die grundlegenden Operationen, wie marshalling, unmarshalling und die Validierung von Datenströmen.

Binding Language: Die Binding Language ist eine XML-basierte Beschreibungssprache, welche die Abbildung eines Schemas auf eine Menge von Klassen spezifiziert. Innerhalb der JAXB-Implementierung können erweiterte Eigenschaften an die Klassen weitergegeben werden. Wie im Praxisbeispiel zu sehen, ist die Beschreibung nicht zwingend notwendig, jedoch sehr zu empfehlen. Aus der Spezifikation von JAXB kann die genaue DTD der Binding Language entnommen werden.

Die Spezifikationen sowie die Referenzimplementierung werden unter dem Java Specification Request JSR-31 (XML Data Binding) zusammengefasst.

Zur Praxis: Mapping und Klassen erstellen
Um in die Praxis einzusteigen, wird als erstes eine JAXB-Implementierung benötigt. Diese kann zum Beispiel von Sun bezogen werden [1]. Zu beachten ist, dass die derzeitige Lösung noch eine Early Access Version darstellt, weshalb mit dem einen oder anderen Problem in jedem Fall gerechnet werden muss. Wie schon beschrieben, kann man sich JAXB als ein Framework zum Mapping zwischen XML-Schemas und Java-Klassen vorstellen. Aus einem vorhandenen Schema werden automatisch die Quelltexte für die Java-Klassen erzeugt. Basis dafür ist die entsprechende DTD-Datei (Document Type Descriptor). In unserem Beispiel gehen wir von einer DTD Datei aus, welche ein Buch beschreibt (siehe auch book.dtd):

<?xml version="1.0" encoding="UTF-8"?>

<!ELEMENT book (chapter+)>
<!ATTLIST book title CDATA #REQUIRED>
<!ATTLIST book author CDATA #REQUIRED>
<!ATTLIST book pageCount CDATA #IMPLIED>

<!ELEMENT chapter (content?)>
<!ATTLIST chapter caption CDATA #REQUIRED>

<!ELEMENT content (#PCDATA)>

Ausgehend von einer Wurzel (root Element - book) wurden verschiedene Unterknoten und Attribute modelliert, welche zusammen das Dokument beschreiben. Im einfachsten Fall kann der vorhandene Document Type Descriptor zur Erzeugung der Klassen genutzt werden. Dazu wird der im Packet enthaltene XJC Compiler genutzt:

java -cp jaxb-xjc-1.0-ea.jar com.sun.tools.xjc.Main book.dtd -roots book

Wie zu erkennen ist, wird lediglich die DTD als Parameter übergeben. Als Ergebnis erhalten wir drei Dateien, für jedes Element eine.

Book.java
Chapter.java
Content.java

Mit obigem Beispiel haben wir gleichzeitig die Minimalvariante genutzt. Im Fall des Elementes content zum Beispiel kann man jetzt natürlich überlegen, ob es tatsächlich als eigene Klasse modelliert werden soll oder ob es ein Attribut der Klasse Chapter ist. Genau so stellt sich natürlich die Frage nach der Package-Bezeichnung. Zum besseren Handling empfehlen sich einige Anpassungen. Grundsätzlich werden diese Anpassungen in einer weiteren XML-Datei mit der Endung xjs hinterlegt. Sind keine Informationen vorhanden, werden Standards, wie im vorherigen Beispiel demonstriert, angenommen. Eine zusätzliche Beschreibungsdatei könnte folgenden Inhalt haben (siehe auch book.xjs):

<?xml version="1.0" encoding="UTF-8"?>

<xml-java-binding-schema version="1.0ea">
<options package="com.test" />
<element name="book" type="class" root="true">
<attribute name="pageCount" convert="int"/>
<content>
<element-ref name="chapter" property="chapters"/>
</content>
</element>
<element name="content" type="value" />
</xml-java-binding-schema>

Diese Datei definiert die folgende Änderungen zum Standard-Mapping:
  • Der Name des Packages für die zu erzeugenden Klassen wird auf com.test festgelegt. Es wird eine Root-Element definiert. Beim Kompilieren mittels XJC kann diese Angabe jetzt weg gelassen werden.

  • Das Attribut pageCount soll als int Type verwendet werden. Innerhalb des erzeugten Parsers wird Code für eine automatische Konvertierung zu und von einem int Wert erzeugt.

  • Die Eigenschaft für die Kapitel bekommt standardmäßig den Namen chapter. Die kleine grammatikalische Unkorrektheit wird zu chapters korrigiert.

  • Das Element content soll nicht als eine Klasse modelliert werden, sondern ist eine Eigenschaft der Klasse Chapter.
Es gibt noch weitere interessante Features für die Generierung der Klassen, so können zum Beispiel auch Konstruktoren definiert werden, die bereits Initialisierungswerte übernehmen können. Auch lassen sich Interfaces erstellen, die zum Beispiel von mehreren Klassen, die alternativ auftreten können, automatisch implementiert werden (z.B.: ). Leider sind in der vorliegenden Version diese Features zum Teil noch nicht ganz ausgereift oder nur teilweise implementiert. Jetzt können die Klassen mit Hilfe des von uns definierten Mappings erzeugt werden. Auch hier kann wieder der schon bekannte XJC Compiler verwendet werden:

java -cp jaxb-xjc-1.0-ea.jar com.sun.tools.xjc.Main book.dtd book.xjs

Die von uns beschriebenen Eigenschaften (Packagename, content als Eigenschaft, ...) führen zu den nachfolgenden zwei Klassen (siehe auch beiliegenden Quelltext).

com.test.Book.java
com.test.Chapter.java

In einer Java-Anwendung können die erzeugten Klassen direkt verwendet werden. Sehr vorteilhaft ist, dass keine Rücksicht auf ihre XML-Abbildung oder strukturelle Eigenschaften (Attribut vs. Element) genommen werden muss:

Book book = new Book();
book.setTitle("JAXB Sample");
book.setAuthor("Java Magazin Autor");
book.setPageCount(5);

Chapter chapter = new Chapter();
chapter.setCaption("Introduction");
chapter.setContent("JAXB is a simple framework for mapping XML to classes.");
book.getChapter().add(chapter);

Nachdem wir jetzt die Klassen erzeugt haben bzw. die Klassen nutzen können, bleibt noch das Lesen und Schreiben der Dateien offen. Auch hier stellt uns JAXB einige die Arbeit erleichternde Hilfsmittel zur Verfügung.

Schreiben und Lesen einer XML-Datei
Das Schreiben der XML-Datei kann über einen beliebigen Java-Stream erfolgen. Vorraussetzung ist jedoch die vorherige Validierung des Dokumentes (Objekt Model):

OutputStream os = new FileOutputStream("book.xml");
book.validate();
book.marshal(os);
os.close();

Nach der Ausführung sollte eine Datei book.xml mit dem durch die Objekte definierten Inhalt entstanden sein. Das Lesen erfolgt ebenfalls über einen beliebigen Java Stream. Von JAXB wird eine statische Methode (unmarshal) in der Root Klasse dafür zur Verfügung gestellt:

InputStream is = new FileInputStream("book.xml");
Book book = Book.unmarshal(is);
is.close();

Fazit
Endlich! Spätestens seit dem Zeitpunkt, da XML immer größere Bedeutung erlangt hat, wünschen sich die meisten Entwickler einen einfachen Mechanismus zur Konvertierung zwischen XML und Java. Es existieren zwar etliche unabhängige Initiativen, jedoch konnten die meisten Entwickler sich nie richtig damit anfreunden (Proprietär, schwierig, ...). Mit JAXB wird eine einheitliche Lösung vorgegeben, welche trotz des frühen Stadiums schon beachtliche Ergebnisse vorzuweisen hat. Jeder, der mit dem Mapping zwischen XML und Java zu tun hat, sollte einen Blick auf die Java Architecture for XML Binding werfen. Es lohnt sich.

Literatur
[1] http://java.sun.com/xml/jaxb/index.html - Java Architecture for XML Binding


    Hat Ihnen dieser Artikel gefallen? Dann abonnieren Sie das Entwickler Magazin direkt über unser Online-Formular.

zur vorherigen Seite
zurück
an den Anfang der Seite
nach oben
Diesen Artikel drucken
drucken
Diesen Artikel weiterempfehlen
empfehlen




Software & Support Verlag GmbH