Mit JAXB einfach von XML Daten zu Java Objekten

Eine Variante mit XML in Java zu arbeiten ist die Java Architecture for XML Binding, besser bekannt unter JAXB. Mit JAXB kann man sehr einfach Java Objekte als XML Datei speichern (marshal) und umgekehrt eine XML Datei wieder in ein Java Objekt laden (unmarshal). So kann man z.B. Daten sehr einfach austauschen oder persistieren. Implementiert man einen Webservice mit JAX-WS (Java API for XML – Web Services) werden z.B. die Daten die ausgetauscht werden mit JAX-B Annotationen versehen und via XML ausgetauscht. Das Umwandeln des Java Objekts zu XML und wieder zurück übernimmt dabei JAXB.

Anwendungsfall: XML Daten zu Java Objekten

In meinem Anwendungsfall habe ich eine Online Schnittstelle die mir die Ergebnisse zu Spieltagen der Bundesliga als XML liefert. Ziel ist es diese Informationen so einfach wie möglich in meine Java Anwendung zu bekommen. Mit Hilfe von JAXB sollen diese XML Informationen zu Java Objekten umgewandelt werden, so dass man damit einfach weiterarbeiten kann. Dieser Vorgang wird unmarshal genannt und erzeugt eine Objekt Struktur die den Inhalt und den Aufbau der XML Datei repräsentiert.

Die erzeugte Objektstruktur ist aber kein DOM basierter Baum, wie manchmal angenommen wird, sondern ein normale Java Objekte, die wieder Referenzen auf weitere Java Objekte haben.

Die XML die ich geliefert bekomme und zu Java Objekten mit JAXB unmarshallen möchte sieht wie folgt aus:

<?xml version="1.0" encoding="UTF-8"?>
<spieltag>
    <game>
        <spieltag>28</spieltag>
        <nummer>1</nummer>
        <beginn>2010-03-26 20:30:00</beginn>
        <mannschaft_heim><![CDATA[VfL Bochum]]></mannschaft_heim>
        <mannschaft_gast><![CDATA[Eintracht Frankfurt]]></mannschaft_gast>
        <tore_heim_halbzeit>1</tore_heim_halbzeit>
        <tore_gast_halbzeit>1</tore_gast_halbzeit>
        <tore_heim_ergebnis>1</tore_heim_ergebnis>
        <tore_gast_ergebnis>2</tore_gast_ergebnis>
    </game>
    <game>
        <spieltag>28</spieltag>
        <nummer>2</nummer>
        <beginn>2010-03-27 15:30:00</beginn>
        <mannschaft_heim><![CDATA[Bayern München]]></mannschaft_heim>
        <mannschaft_gast><![CDATA[VfB Stuttgart]]></mannschaft_gast>
        <tore_heim_halbzeit></tore_heim_halbzeit>
        <tore_gast_halbzeit></tore_gast_halbzeit>
        <tore_heim_ergebnis></tore_heim_ergebnis>
        <tore_gast_ergebnis></tore_gast_ergebnis>
    </game>
    ...
</spieltag>

Um mit JAXB von der XML Datei zu Java Objekten zu gelangen benötigt man die Beschreibung des Mappings mit JAXB Annotationen in Java Klassen. Da in diesem Fall die XML Datei vorliegt ist der einfachste Weg sich die Klassen mit den Annotationen generieren zu lassen. Dazu benötigt man das XML Schema zu zu der XML Datei.

XML Schema aus einer XML Datei generieren lassen

In meinem Fall habe noch kein Schema für diese XML Struktur. Eines jetzt von Hand zu erstellen wäre möglich aber sehr aufwendig. An dieser Stelle kommt das kleine Java Tool Namens Trang ins Spiel.

Dazu die letzte Version aus 2008 herunterladen (Direktlink) und an einem Ort der Wahl entpacken. Anschließend die XML Datei der Einfachheit halber auch mit in diese Ordner kopieren und darauf achten, dass sie mit UTF-8 enkodiert ist. Um das Schema zu erzeugen einfach das trang.jar mit zwei Parametern aufrufen:

D:\projects\trang-20081028>java -jar trang.jar spieltag.xml spieltag.xsd

Das von Trang generierte Schema sieht bei mir wie folgt aus:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <xs:element name="spieltag">
    <xs:complexType mixed="true">
      <xs:sequence>
        <xs:element minOccurs="0" maxOccurs="unbounded" ref="game"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="game">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="spieltag"/>
        <xs:element ref="nummer"/>
        <xs:element ref="beginn"/>
        <xs:element ref="mannschaft_heim"/>
        <xs:element ref="mannschaft_gast"/>
        <xs:element ref="tore_heim_halbzeit"/>
        <xs:element ref="tore_gast_halbzeit"/>
        <xs:element ref="tore_heim_ergebnis"/>
        <xs:element ref="tore_gast_ergebnis"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="nummer" type="xs:integer"/>
  <xs:element name="beginn" type="xs:string"/>
  <xs:element name="mannschaft_heim" type="xs:string"/>
  <xs:element name="mannschaft_gast" type="xs:string"/>
  <xs:element name="tore_heim_halbzeit" type="xs:string"/>
  <xs:element name="tore_gast_halbzeit" type="xs:string"/>
  <xs:element name="tore_heim_ergebnis" type="xs:string"/>
  <xs:element name="tore_gast_ergebnis" type="xs:string"/>
</xs:schema>
Java Klassen mit JAXB Annotationen aus XML Schema generieren

Das JDK liefert einen Binding Compiler für JAXB mit dem Namen XJC mit aus der im /bin Verzeichnis unterhalb der Java Runtime zu finden ist. Damit kann man aus einem XML Schema die entsprechenden Java Klassen generieren lassen, aus denen dann beim späteren unmarshal Prozess die Java Objekte erstellt werden.

Der Aufruf des Binding Compiler verwendet in diesem Beispiel zwei Parameter. Der Paramter d definiert das Verzeichnis in dem die Klassen gespeichert werden sollen, bei mir das Verzeichnis src relativ zum aktuellen Verzeichnis, und der Paramter p definiert das Java Package in dem die Klassen liegen sollen. Als letztes wird das XML Schema angegeben aus dem die Java Klassen generiert werden sollen:

D:\projects\TipResultParser\xml>xjc -d src -p de.javathreads.jaxb spieltag.xsd

Für die obige XML Datei wurden drei Klassen generiert:

Im folgenden der interessante Teil aus der klasse Game mit JAXB Annotationen:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "spieltag",
    "nummer",
    "beginn",
    "mannschaftHeim",
    "mannschaftGast",
    "toreHeimHalbzeit",
    "toreGastHalbzeit",
    "toreHeimErgebnis",
    "toreGastErgebnis"
})
@XmlRootElement(name = "game")
public class Game {

    @XmlElement(required = true)
    protected Spieltag spieltag;
    @XmlElement(required = true)
    protected BigInteger nummer;
    @XmlElement(required = true)
    protected String beginn;
    @XmlElement(name = "mannschaft_heim", required = true)
    protected String mannschaftHeim;
    @XmlElement(name = "mannschaft_gast", required = true)
    protected String mannschaftGast;
    @XmlElement(name = "tore_heim_halbzeit", required = true)
    protected String toreHeimHalbzeit;
    @XmlElement(name = "tore_gast_halbzeit", required = true)
    protected String toreGastHalbzeit;
    @XmlElement(name = "tore_heim_ergebnis", required = true)
    protected String toreHeimErgebnis;
    @XmlElement(name = "tore_gast_ergebnis", required = true)
    protected String toreGastErgebnis;

    // Getter und Setter
}

Man kann an diesem Beispiel sehr schön das Mapping zwischen XML Element und Attribut mit der Annotation @XmlElement sehen:

@XmlElement(name = “mannschaft_heim”, required = true)

Es gibt noch weitaus komplizierte JAXB Konfigurationen von daher empfiehlt es sich eigentlich immer sich die fehlende Gegenseite, wie in diesem Beispiel die Java Klassen, generieren zu lassen. Auch der andere Weg, das marshalling von Java Objektbäumen zu einer XML Datei, lässt sich sehr einfach generieren.

JAXB unmarshal durchführen und mit Java Objekten arbeiten

Nun kommt der wichtigste Teil des Tutorials, nämlich die eigentliche Anwendung von JAXB. Die XML Datei soll als Java Objektstruktur zur Verfügung stehen, so dass die Daten einfach weiterverarbeitet werden können. Dieser Weg wird, wie am Anfang beschrieben, unmarshal genannt. Bis zum Java Objekt sind es an dieser Stelle nur noch drei Zeilen:

Jetzt kann man direkt mit der Objektrepräsentation der XML Datei auf Java Ebene arbeiten. Noch einmal der Hinweis, dass es sich bei diesen Objekten nicht um ein DOM basierten Baum handelt sondern um reine Content-Objekte, welche den Inhalt und die Struktur der XML Datei wiedergeben.

Als letztes noch mal der komplette Code an einem Stück für die bessere Übersicht:

package de.javathreads.jaxb;

import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;

public class Main {

  public static void main(String[] args) throws Exception {
    // Package
    JAXBContext jc = JAXBContext.newInstance("de.javathreads.jaxb");
    Unmarshaller unmarshaller = jc.createUnmarshaller();

    Spieltag sp = (Spieltag) unmarshaller.unmarshal(new File("D:\\projects\\TipResultParser\\xml\\spieltag.xml"));

    for (int i = 0; i < sp.getContent().size(); i++) {
      if (sp.getContent().get(i) instanceof Game) {
        Game g = (Game) sp.getContent().get(i);
        System.out.println("Spiel: " + g.getNummer());
      }
    }
  }
}

Ausgabe auf der Konsole:

Spiel: 1
Spiel: 2

Hinweis zu dem Code bezüglich der Prüfung mit instanceof Game. Die XML Schnittstelle liefert den Inhalt formatiert und somit mit Sonderzeichen wie “\t” und “\n” zurück, so dass diese Zeichen als Content direkt hinter dem XML Tag beim unmarshal Prozess erkannt wurden. Die Liste, die mir sp.getContent() zurückliefert, enthält abwechselnd den String “\t\t\n” und ein Game Objekt. Das ist aber nicht die Regel und ich muss mal schauen ob ich dieses Verhalten nicht noch irgendwie abstellen kann.

Die Grafiken wurden aus dem offiziellen Sun Artikel “Java Architecture for XML Binding (JAXB)” entnommen. Dort findet man auch eine ausführliche Anleitung für den marhall Vorgang, wenn man Java Objekte zu einer XML Datei marshallen möchte.

Tags: , , ,

Wenn du Fragen oder Anregungen zum Post hast, dann hinterlasse doch einen Kommentar oder wenn du weiterhin Artikel von Javathreads lesen möchtest, dann abonniere den RSS Feed und sehe direkt in deinem Feed Reader die nächsten Artikel.

Ähnliche Artikel, die dich interessieren könnten:
Kommentare

Die spannendsten Fragen wurden leider nicht angeschnitten: Was passiert, wenn sich das zu speichernde Objekt ändert, wenn Attribute im Code weggenommen oder ergänzt werden? Das sich dann die Schema-Dateien ändern ist klar, aber was passiert mit schon als XML abgelegten Objekten? Kann ich diese nach wie vor einlesen, was passiert mit fehlenden oder zusätzlichen Attributen? Wird dann ein Fehler geworfen oder neue Attribute schlicht nicht initialisiert, werden überflüssige Daten weggeworfen? Kann ich dieses Verhalten beeinflussen? Das sind die interessanten Fragen, die man sich bei jedem Persistierungsframework im weitesten Sinne stellen muss. Wenn man einmal eine Menge Daten hat und dann an den Datenstrukturen was ändern muss, und erst dann anfängt, sich darüber Gedanken zu machen, wie das eigentlich geht, ist es zu spät.

Stimmt, in dem Artikel wird nicht darauf eingegangen weil ich es lediglich zum einlesen von XML Daten einer Schnittstelle benötigte und ich einfach nicht daran gedacht habe ;). Aber die Frage ist berechtigt: ändern sich die Daten bzw. die Struktur in der Schnittstelle ist die Frage nach der Behandlung durch JAXB durchaus wichtig.

Ohne jetzt auf jedes einzelne einzugehen haben erste Tests ergeben, dass JAXB ziemlich tolerant ist. Kommt ein Attribut hinzu oder entfällt hat das keine weiteren Auswirkungen sondern das Attribut wird einfach ignoriert. Entfällt eine ganzes Element, die als eigene Klasse repräsentiert wird, wird diese auch einfach ignoriert.

Aber die Frage ist durchaus interessant – muss mir das nochmal genauer ansehen und ein paar Tests machen.

[...] Dieser Eintrag wurde auf Twitter von Simon Knoll erwähnt. Simon Knoll sagte: http://javathreads.de/2010/04/mit-jaxb-einfach-von-xml-daten-zu-java-objekten/ interessant #java #xml [...]

Ein interessanter und gut gemachter Beitrag zum Thema JAXB. Glückwunsch und weiter so Herr Kühle!

Gestolpert bin ich lediglich über die gewählte Datenstruktur der XML-Datei. Es wird zweimal ein Tag verwendet. Einmal dient dieser als Container für beliebig viele ’s, und zum anderen befindet sich innerhalb eines ’s wiederum ein Element . Diese Strukturierung ist aus meiner Sicht nicht richtig und verwirrt. Da letztlich zwei unterschiedliche Dinge gemeint sind, aber beide über den selben Tag verfügen. Ich würde erwarten, dass es bei dem (Container-)Tag ein Attribut gibt, in dem man die Zahl des Spieltags einmalig einträgt. Alle ’s dieses Spieltages sind dem Spieltag ja bereits eindeutig zugeordnet, und benötigten nicht noch mal diese redundante Information.

Mit ’s habe ich jeweils game’s gemeint. Diese werden jedoch nicht als solche dargestellt, wenn ich das Wort game mittels klammere.

Du hast absolut recht mit dem, auch meiner Meinung nach, falsch verwendetem Tag “Spieltag” innerhalb von “Game”. Die Spieltagsnummer gehört, wie schon von dir gesagt, meiner Meinung nach als Attribut in das obere Container Element “Spieltag”.

Ich habe dieses XML exakt so von der Schnittstelle bekommen und leider leider, wie ich jetzt meine, für den Artikel nicht bereinigt.

Man erkennt übrigens auch den Fehler in der generierten Java Klasse “Game” der sich aus dem doppelt verwendeten “Spieltag” ergibt: JAXB meint, dass es sich hierbei um eine Referenz auf das Container Element handelt und erkennt nicht, dass es eigentlich im XML eine Nummer ist.
In meiner Umsetzung der Schnittstelle habe ich diesen Fehler manuell aus den generierten JAXB Klassen entfernt.

Das es dir aufgefallen ist sagt mir, dass du den Artikel auch wirklich gelesen hast ;) – und somit auch vielen Dank für die Bemerkung!

Herzlichen Glückwunsch zu diesem Artikel! Das hat mir genau den Einstieg in Java mit XML ermöglicht, den ich gesucht habe. Für den Anfang finde ich es immer sehr wichtig, dass eben nicht alles bis ins Detail besprochen wird. Ich möchte eine einfache Einführung, auf der ich dann selbst aufbauen kann. Genau diese habe ich hier gefunden! Weiter so!

Toller Beitrag zur JAXB Thematik. Gestoßen bin ich auf den Artikel als ich nach einem Tool gesucht habe um aus XML XSD Dateien zu erzeugen, damit ich mir JAXB Objekte erzeugen kann.

Leider finde ich keinen Flattr Button ;)

Wunderbar! Schöner, detaillierter Beitrag mit guten Code-Beispielen! Hat sofort funktioniert und mir einige Stunden gerettet. Vielen Dank dafür! Das “Trang”-Tool ist wirklich klasse!

Super erklärt!!! 1A …Vielen Dank.

Hinterlasse einen Kommentar