Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.
 
Inhaltsverzeichnis
Vorwort
1 Neues in Java 8 und Java 7
2 Fortgeschrittene String-Verarbeitung
3 Threads und nebenläufige Programmierung
4 Datenstrukturen und Algorithmen
5 Raum und Zeit
6 Dateien, Verzeichnisse und Dateizugriffe
7 Datenströme
8 Die eXtensible Markup Language (XML)
9 Dateiformate
10 Grafische Oberflächen mit Swing
11 Grafikprogrammierung
12 JavaFX
13 Netzwerkprogrammierung
14 Verteilte Programmierung mit RMI
15 RESTful und SOAP-Web-Services
16 Technologien für die Infrastruktur
17 Typen, Reflection und Annotationen
18 Dynamische Übersetzung und Skriptsprachen
19 Logging und Monitoring
20 Sicherheitskonzepte
21 Datenbankmanagement mit JDBC
22 Java Native Interface (JNI)
23 Dienstprogramme für die Java-Umgebung
Stichwortverzeichnis

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java SE 8 Standard-Bibliothek von Christian Ullenboom
Das Handbuch für Java-Entwickler
Buch: Java SE 8 Standard-Bibliothek

Java SE 8 Standard-Bibliothek
Pfeil 8 Die eXtensible Markup Language (XML)
Pfeil 8.1 Auszeichnungssprachen
Pfeil 8.1.1 Die Standard Generalized Markup Language (SGML)
Pfeil 8.1.2 Extensible Markup Language (XML)
Pfeil 8.2 Eigenschaften von XML-Dokumenten
Pfeil 8.2.1 Elemente und Attribute
Pfeil 8.2.2 Beschreibungssprache für den Aufbau von XML-Dokumenten
Pfeil 8.2.3 Schema – die moderne Alternative zu DTD
Pfeil 8.2.4 Namensraum (Namespace)
Pfeil 8.2.5 XML-Applikationen *
Pfeil 8.3 Die Java-APIs für XML
Pfeil 8.3.1 Das Document Object Model (DOM)
Pfeil 8.3.2 Simple API for XML Parsing (SAX)
Pfeil 8.3.3 Pull-API StAX
Pfeil 8.3.4 Java Document Object Model (JDOM)
Pfeil 8.3.5 JAXP als Java-Schnittstelle zu XML
Pfeil 8.3.6 DOM-Bäume einlesen mit JAXP *
Pfeil 8.4 Java Architecture for XML Binding (JAXB)
Pfeil 8.4.1 Bean für JAXB aufbauen
Pfeil 8.4.2 Utility-Klasse JAXB
Pfeil 8.4.3 Ganze Objektgraphen schreiben und lesen
Pfeil 8.4.4 JAXBContext und Marshaller/Unmarshaller nutzen
Pfeil 8.4.5 Validierung
Pfeil 8.4.6 Weitere JAXB-Annotationen *
Pfeil 8.4.7 Beans aus XML-Schema-Datei generieren
Pfeil 8.5 Serielle Verarbeitung mit StAX
Pfeil 8.5.1 Unterschiede der Verarbeitungsmodelle
Pfeil 8.5.2 XML-Dateien mit dem Cursor-Verfahren lesen
Pfeil 8.5.3 XML-Dateien mit dem Iterator-Verfahren verarbeiten *
Pfeil 8.5.4 Mit Filtern arbeiten *
Pfeil 8.5.5 XML-Dokumente schreiben
Pfeil 8.6 Serielle Verarbeitung von XML mit SAX *
Pfeil 8.6.1 Schnittstellen von SAX
Pfeil 8.6.2 SAX-Parser erzeugen
Pfeil 8.6.3 Operationen der Schnittstelle ContentHandler
Pfeil 8.6.4 ErrorHandler und EntityResolver
Pfeil 8.7 XML-Dateien mit JDOM verarbeiten
Pfeil 8.7.1 JDOM beziehen
Pfeil 8.7.2 Paketübersicht *
Pfeil 8.7.3 Die Document-Klasse
Pfeil 8.7.4 Eingaben aus der Datei lesen
Pfeil 8.7.5 Das Dokument im XML-Format ausgeben
Pfeil 8.7.6 Der Dokumenttyp *
Pfeil 8.7.7 Elemente
Pfeil 8.7.8 Zugriff auf Elementinhalte
Pfeil 8.7.9 Liste mit Unterelementen erzeugen *
Pfeil 8.7.10 Neue Elemente einfügen und ändern
Pfeil 8.7.11 Attributinhalte lesen und ändern
Pfeil 8.7.12 XPath
Pfeil 8.8 Transformationen mit XSLT *
Pfeil 8.8.1 Templates und XPath als Kernelemente von XSLT
Pfeil 8.8.2 Umwandlung von XML-Dateien mit JDOM und JAXP
Pfeil 8.9 XML-Schema-Validierung *
Pfeil 8.9.1 SchemaFactory und Schema
Pfeil 8.9.2 Validator
Pfeil 8.9.3 Validierung unterschiedlicher Datenquellen durchführen
Pfeil 8.10 Zum Weiterlesen
 
Zum Seitenanfang

8.4Java Architecture for XML Binding (JAXB) Zur vorigen ÜberschriftZur nächsten Überschrift

Java Architecture for XML Binding (JAXB) ist eine API zum Übertragen von Objektzuständen auf XML-Dokumente und umgekehrt. Anders als eine manuelle Abbildung von Java-Objekten auf XML-Dokumente oder das Parsen von XML-Strukturen und Übertragen der XML-Elemente auf Geschäftsobjekte arbeitet JAXB automatisch. Die Übertragungsregeln definieren Annotationen, die Entwickler selbst an die JavaBeans setzen können, aber JavaBeans werden gleich zusammen mit den Annotationen von einem Werkzeug aus einer XML-Schema-Datei generiert.

JAXB läuft in anderen Versionsnummern als das JDK. Java 7 aktualisiert auf JAXB 2.2, die gleiche Version, die auch Java 8 hat.

 
Zum Seitenanfang

8.4.1Bean für JAXB aufbauen Zur vorigen ÜberschriftZur nächsten Überschrift

Wir wollen einen Player deklarieren, und JAXB soll ihn anschließend in ein XML-Dokument übertragen:

Listing 8.6com/tutego/insel/xml/jaxb/Player.java, Player

@XmlRootElement
class Player {

private String name;
private Date birthday;

public String getName() {
return name;
}

public void setName( String name ) {
this.name = name;
}

public void setBirthday( Date birthday ) {
this.birthday = birthday;
}

public Date getBirthday() {
return birthday;
}
}

Die Klassen-Annotation @XmlRootElement ist an der JavaBean nötig, wenn die Klasse das Wurzelelement eines XML-Baums bildet. Die Annotation stammt aus dem Paket javax.xml.bind.annotation.

 
Zum Seitenanfang

8.4.2Utility-Klasse JAXB Zur vorigen ÜberschriftZur nächsten Überschrift

Ein kleines Testprogramm baut eine Person auf und bildet sie dann in XML ab – die Ausgabe der Abbildung kommt auf den Bildschirm. Um ein Objekt in seine XML-Repräsentation zu bringen, lässt sich auf eine einfache statische marshall(…)-Methode der Utility-Klasse JAXB zurückgreifen:

Listing 8.7com/tutego/insel/xml/xml/jaxb/SimplePlayerMarshaller.java, main()

Player johnPeel = new Player();
johnPeel.setName( "John Peel" );
johnPeel.setBirthday( new GregorianCalendar(1939,Calendar.AUGUST,30).getTime() );
JAXB.marshal( johnPeel, System.out );

JAXB beachtet standardmäßig alle Bean-Properties, also birthday und name, und nennt die XML-Elemente nach den Properties:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<player>
<birthday>1939-08-30T00:00:00+01:00</birthday>
<name>John Peel</name>
</player>

Die marshall(…)-Methode schreibt das in XML transformierte Player-Objekt in unserem Beispiel in den Konsolenausgabestrom. Die JAXB-Klasse bietet daneben auch unmarshall(…)-Methoden, um das Objekt wiederherzustellen, das schauen wir uns im nächsten Beispiel an.

Falls das Schreiben oder Lesen misslingt, gibt es eine RuntimeException. Diese referenziert die intern geprüften Ausnahmen vom Typ DataBindingException.

class javax.xml.bind.JAXB
  • static void marshal(Object jaxbObject, File xml)

  • static void marshal(Object jaxbObject, OutputStream xml)

  • static void marshal(Object jaxbObject, Result xml)

  • static void marshal(Object jaxbObject, String xml)

  • static void marshal(Object jaxbObject, URI xml)

  • static void marshal(Object jaxbObject, URL xml)

  • static void marshal(Object jaxbObject, Writer xml)
    Schreibt das XML-Dokument in die angegebene Ausgabe. Im Fall von URI/URL wird ein HTTP-POST gestartet. Ist der Parametertyp String, wird er als URL gesehen und führt ebenfalls zu einem HTTP-Zugriff. Result ist ein Typ für eine XML-Transformation und wird später vorgestellt.

  • static <T> T unmarshal(File xml, Class<T> type)

  • static <T> T unmarshal(InputStream xml, Class<T> type)

  • static <T> T unmarshal(Reader xml, Class<T> type)

  • static <T> T unmarshal(Source xml, Class<T> type)

  • static <T> T unmarshal(String xml, Class<T> type)

  • static <T> T unmarshal(URI xml, Class<T> type)

  • static <T> T unmarshal(URL xml, Class<T> type)
    Rekonstruiert aus der gegebenen XML-Quelle den Java-Objektgraphen.

 
Zum Seitenanfang

8.4.3Ganze Objektgraphen schreiben und lesen Zur vorigen ÜberschriftZur nächsten Überschrift

JAXB bildet nicht nur das zu schreibende Objekt ab, sondern auch rekursiv alle referenzierten Unterobjekte. Wir wollen den Spieler dazu in einen Raum setzen und den Raum in XML abbilden. Dazu muss der Raum die Annotation @XmlRootElement bekommen, und bei Player kann sie entfernt werden, wenn nur der Raum selbst, aber keine Player als Wurzelobjekte zum Marshaller kommen:

Listing 8.8com/tutego/insel/xml/xml/jaxb/Room.java, Room

@XmlRootElement( namespace = "http://tutego.com/" )
public class Room {

private List<Player> players = new ArrayList<>();

@XmlElement( name = "player" )
public List<Player> getPlayers() {
return players;
}

public void setPlayers( List<Player> players ) {
this.players = players;
}
}

Zwei Annotationen kommen vor: Da Room der Start des Objektgraphen ist, trägt es @XmlRootElement. Als Erweiterung ist das Element namespace für den Namensraum gesetzt, da bei eigenen XML-Dokumenten immer ein Namensraum genutzt werden soll. Weiterhin ist eine Annotation @XmlElement am Getter getPlayers() platziert, um den Namen des XML-Elements zu überschreiben, damit das XML-Element nicht <players> heißt, sondern <player>.

Kommen wir abschließend zu einem Beispiel, das einen Raum mit zwei Spielern aufbaut und diesen Raum dann in eine XML-Datei schreibt:

Listing 8.9com/tutego/insel/xml/jaxb/RoomMarshaller.java, main()

Player john = new Player();
john.setName( "John Peel" );

Player tweet = new Player();
tweet.setName( "Zwitscher Zoe" );

Room room = new Room();
room.setPlayers( Arrays.asList( john, tweet ) );

Path path = Paths.get( "room.xml" );
try ( Writer out = Files.newBufferedWriter( path, StandardCharsets.UTF_8 ) ) {
JAXB.marshal( room, out );

Room room2 = JAXB.unmarshal( path.toFile(), Room.class );
System.out.println( room2.getPlayers().get( 0 ).getName() ); // John Peel
}
catch ( IOException e ) {
e.printStackTrace();
}

Die Ausgabe ist:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:room xmlns:ns2="http://tutego.com/">
<player>
<name>John Peel</name>
</player>
<player>
<name>Zwitscher Zoe</name>
</player>
</ns2:room>

Da beim Spieler das Geburtsdatum nicht gesetzt war (null wird referenziert), wird es auch nicht in XML abgebildet.

 
Zum Seitenanfang

8.4.4JAXBContext und Marshaller/Unmarshaller nutzen Zur vorigen ÜberschriftZur nächsten Überschrift

Die Utility-Klasse JAXB verfügt ausschließlich über statische überladene marshal(…)- und unmarshal(…)-Methoden, ist aber in Wirklichkeit nur eine Fassade. Vielmehr ist JAXBContext das tatsächliche Ausgangsobjekt, und davon erfragt werden ein Marshaller zum Schreiben und ein Unmarshaller zum Lesen:

Listing 8.10com/tutego/insel/xml/xml/jaxb/PlayerMarshaller.java, main()

Player johnPeel = new Player();
johnPeel.setName( "John Peel" );
johnPeel.setBirthday( new GregorianCalendar(1939,Calendar.AUGUST,30).getTime() );
JAXBContext context = JAXBContext.newInstance( Player.class );
Marshaller m = context.createMarshaller();
m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
m.marshal( johnPeel, System.out );

Die Ausgabe ist identisch mit der ersten:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<player>
<birthday>1939-08-30T00:00:00+01:00</birthday>
<name>John Peel</name>
</player>

Alles bei JAXB beginnt mit der zentralen Klasse JAXBContext. Die statische Methode JAXBContext.newInstance(Class...) erwartet standardmäßig eine Aufzählung der Klassen, die JAXB behandeln soll; die Klassennamen können auch voll qualifiziert über einen String angegeben werden mit newInstance(String). Der JAXBContext liefert den Marshaller zum Schreiben und den Unmarshaller zum Lesen. Die Fabrikmethode createMarshaller() gibt dabei einen Schreiberling, der mit marshal(Object, …) das Wurzelobjekt in einen Datenstrom schreibt. Das zweite Argument von marshal(Object, …) ist unter anderem ein OutputStream (wie System.out in unserem Beispiel), Writer oder File-Objekt.

class javax.xml.bind.JAXBContext
  • static JAXBContext newInstance(Class... classesToBeBound) throws JAXBException
    Liefert ein Exemplar vom JAXBContext mit Klassen, die als Wurzelklassen für JAXB verwendet werden können.

  • abstract Marshaller createMarshaller()
    Erzeugt einen Marshaller, der Java-Objekte in XML-Dokumente konvertieren kann.

  • abstract Unmarshaller createUnmarshaller()
    Erzeugt einen Unmarshaller, der XML-Dokumente in Java-Objekte konvertiert.

class javax.xml.bind.Marshaller
  • void marshal(Object jaxbElement, File output)

  • void marshal(Object jaxbElement, OutputStream os)

  • void marshal(Object jaxbElement, Writer writer)
    Schreibt den Objektgraphen von jaxbElement in eine Datei oder einen Ausgabestrom.

  • void marshal(Object jaxbElement, Node node)
    Erzeugt vom Objekt einen DOM-Knoten. Der kann dann in ein XML-Dokument gesetzt werden.

  • void marshal(Object jaxbElement, XMLEventWriter writer)

  • void marshal(Object jaxbElement, XMLStreamWriter writer)
    Erzeugt für ein jaxbElement einen Informationsstrom für den XMLEventWriter bzw. XMLStreamWriter. Die StAX-Klassen werden später genauer vorgestellt.

  • void setProperty(String name, Object value)
    Setzt eine Eigenschaft der Marshaller-Implementierung. Eine Einrückung etwa setzt das Paar Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE.

Der Unmarshaller bietet über zehn Varianten von unmarshal(…), unter anderem mit den Parametertypen File, InputSource, InputStream, Node, Reader, URL, XMLEventReader.

Tipps uns Tricks mit JAXBContext

Den JAXBContext aufzubauen kostet Zeit und Speicher. Er sollte daher für wiederholte Operationen gespeichert werden. Noch eine Information: Marshaller und Unmarshaller sind nicht threadsicher; es darf keine zwei Threads geben, die gleichzeitig den Marshaller/Unmarshaller nutzen.

Die unmarshal(…)-Methode ist überladen mit Parametern, die typische Datenquellen repräsentieren, etwa Dateien oder Eingabeströme wie ein Reader. Allerdings sind noch andere Parametertypen interessant, und es lohnt sich, hier einmal in die API-Dokumentation zu schauen. Ein spannender Typ ist javax.xml.transform.Source bzw. die Implementierung der Schnittstelle durch JAXBSource. JAXBSource ist die Quelle, aus der JAXB seine Informationen bezieht, um ein neues Java-Objekt zu rekonstruieren.

Das nächste Beispiel nimmt sich das aus dem vorangehenden Beispiel aufgebaute Objekt room als Basis und erzeugt eine tiefe Kopie davon:

Listing 8.11com/tutego/insel/xml/xml/jaxb/RoomCopy.java, main() Ausschnitt

Room room = …
JAXBContext context = JAXBContext.newInstance( Room.class );
Unmarshaller unmarshaller = context.createUnmarshaller();
JAXBSource source = new JAXBSource( context, room );
Room copiedRoom = Room.class.cast( unmarshaller.unmarshal( source ) );
System.out.println( copiedRoom.getPlayers() ); // [com.tutego.insel.xml.jaxb.Player@...]

Das Beispiel zeigt somit, wie sich mithilfe von JAXB Objektkopien erzeugen lassen.

 
Zum Seitenanfang

8.4.5Validierung Zur vorigen ÜberschriftZur nächsten Überschrift

Beim Schreiben und auch beim Lesen von XML-Dokumenten kann JAXB diese gegen ein XML-Schema validieren:

  • Falsche XML-Dokumente sollen nicht eingelesen werden. Wenn die Schema-Datei zum Beispiel vorschreibt, dass eine Zahl (etwa für die Bohrtiefe) nur im Wertebereich von 0 bis 10.000 liegen darf, und in einer XML-Datei taucht dann ein Wert von 10.600 auf, dann wäre die XML-Datei nach diesem Schema nicht valide. JAXB sollte sie ablehnen.

  • Falsche Werte in JavaBeans dürfen nicht zu nichtvaliden XML-Dokumenten führen. JavaBeans bestehen aus einfachen Settern und Gettern, und die Prüfungen im XML-Schema gehen weit über das hinaus, was üblicherweise eine JavaBean prüft. So kann eine Schema-Definition etwa vorschreiben, dass eine Zeichenkette nach einem ganz speziellen regulären Ausdruck geschrieben wird. In der Regel sind die Setter aber nicht so implementiert, dass sie die Strings direkt prüfen. JAXB sollte es auch nicht erlauben, dass JavaBeans mit falschen Strings geschrieben werden und zu nichtvaliden XML-Dokumenten führen können.

Zentral bei der Validierung ist eine XML-Schema-Datei. Doch wo kommt diese her? Wurden aus der Schema-Datei die JavaBeans generiert, ist logischerweise die Schema-Beschreibung schon da. Sind die JavaBeans aber zuerst da, gibt es erst einmal keine Schema-Datei. Über unterschiedliche Wege lässt sich eine passende Schema-Datei entwickeln:

  • Per Hand: Die XML-Schema-Datei wird per Hand selbstständig ohne Tool entwickelt.

  • Über die XML-Dokumente: Ein Tool analysiert XML-Dateien und erzeugt aufgrund der erkannten Strukturen eine XML-Schema-Datei. Für dieses Verfahren gibt es eine Reihe von Tools, und einige sind auch online verfügbar, etwa http://tutego.de/go/xml-2-xsd.

  • Mit schemagen: Das JDK bringt ein Tool mit dem Namen schemagen mit, das auf JAXB-annotierte Beans angesetzt wird und die Schema-Datei generiert.

Mit schemagen aus JAXB-Beans eine XML-Schema-Datei generieren

Das Tool schemagen befindet sich wie alle anderen JDK-Tools im bin-Verzeichnis. Das Kommandozeilentool erwartet die Angabe einer Quellcodeklasse oder einer compilierten Klasse und spuckt die Schema-Beschreibungen aus. Kapseln wir das in ein Skript:

Listing 8.12room-schemagen.bat

@echo off
PATH=%PATH%;C:\Program Files\Java\jdk1.8.0\bin
schemagen com.tutego.insel.xml.jaxb.Room

Und rufen wir es auf:

$ room-schemagen.bat
Note: Writing C:\...\schema1.xsd
Note: Writing C:\...\schema2.xsd

Das Tool erzeugt zwei Schema-Dateien, und sie sehen so aus:

Listing 8.13schema1.xsd

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" targetNamespace="http://tutego.com/"
xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:import schemaLocation="schema2.xsd" />

<xs:element name="room" type="room" />

</xs:schema>

Listing 8.14schema2.xsd

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:element name="player" type="player" />

<xs:complexType name="room">
<xs:sequence>
<xs:element ref="player" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>

<xs:complexType name="player">
<xs:sequence>
<xs:element name="birthday" type="xs:dateTime" minOccurs="0" />
<xs:element name="name" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:schema>

Ein genaues Verständnis des Schemas ist nicht nötig.

Visualisierung des Schemas

Abbildung 8.1Visualisierung des Schemas

Schema-Validierung mit JAXB

Wir wollen die Validierungsmöglichkeiten von JAXB für unsere bekannte Raum-Datei ausprobieren und bewusst zwei Fehler einbauen (siehe Tabelle 8.4).

Valide XML-Datei (nach Schema)

Nichtvalide XML-Datei (nach Schema)

Listing 8.15room.xml

<?xml version="1.0" encoding="UTF-8"?>
<ns2:room xmlns:ns2="http://tutego.com/">
<player>
<name>John Peel</name>
</player>
<player>
<name>Zwitscher Zoe</name>
</player>
</ns2:room>

Listing 8.16room-invalid.xml

<?xml version="1.0" encoding="UTF-8"?>
<room>
<player>
<name>John Peel</name>
</player>
<player>
<name>Zwitscher Zoe</name>
<name>Heini Hayward</name>
</player>
</room>

Tabelle 8.4Nach dem Schema valide und nichtvalide XML-Datei

Das Java-Programm aus dem vorigen Abschnitt schrieb eine korrekte XML-Datei room.xml. In room-invalid.xml fehlt einmal der Namensraum, und dann sind zwei Namen angegeben, obwohl die Schema-Datei nur einen Namen erlaubt.

Damit JAXB den Fehler erkennt, muss es mit der neuen Schema-Datei verbunden werden. JAXP hat eine eigene API für Validierungen, die dafür eingesetzt wird (mehr zur Schema-Validierung folgt später in Abschnitt 8.9, »XML-Schema-Validierung *«):

Listing 8.17com/tutego/insel/xml/jaxb/ValidatingRoomUnmarshaller.java, main()

JAXBContext context = JAXBContext.newInstance( Room.class );
Unmarshaller unmarshaller = context.createUnmarshaller();
SchemaFactory sf = SchemaFactory.newInstance( W3C_XML_SCHEMA_NS_URI );
Schema schema = sf.newSchema(
ValidatingRoomUnmarshaller.class.getResource( "/schema1.xsd" ) );
unmarshaller.setSchema( schema );
URL url = Paths.get( "invalid-room.xml" ).toUri().toURL();
Room room = (Room) unmarshaller.unmarshal( url );
System.out.println( room.getPlayers() );

Es wird ein Exemplar eines Schema-Objekts erzeugt und dieses über setSchema(Schema) beim Unmarshaller gesetzt. (Achtung: JAXB.unmarshal(file, Room.class) wird nicht funktionieren!)

Schon der erste Fehler in invalid-room.xml führt zum Abbruch:

Exception in thread "main" javax.xml.bind.UnmarshalException
– with linked exception:
[org.xml.sax.SAXParseException; systemId: ¿
file:/C:/Insel/programme/18_XML/invalid-room.xml; lineNumber: 2; columnNumber: 7; ¿
cvc-elt.1: Cannot find the declaration of element 'room'.]

Ist der Fehler behoben, kommt das zweite Problem zum Tragen, nämlich der Umstand, dass es zwei Namen gibt:

Exception in thread "main" javax.xml.bind.UnmarshalException
– with linked exception:
[org.xml.sax.SAXParseException; systemId: ¿
file:/C:/Insel/programme/18_XML/invalid-room.xml; lineNumber: 8; columnNumber: 11; ¿
cvc-complex-type.2.4.d: Invalid content was found starting with element 'name'. ¿
No child element is expected at this point.]

Erst wenn der Fehler behoben wurde, gibt es keine Ausnahme mehr, und JAXB gibt Ruhe.

 
Zum Seitenanfang

8.4.6Weitere JAXB-Annotationen * Zur vorigen ÜberschriftZur nächsten Überschrift

XML-Schemas können recht komplex werden, sodass auch die Anzahl der JAXB-Annotationen und Möglichkeiten hoch ist. Im Folgenden sollen verschiedene JAXB-Annotationen ihre Wirkung auf die XML-Ausgaben zeigen.

Zugriff über Setter/Getter oder Attribute

JAXB kann sich die Werte über JavaBean-Properties – also Setter/Getter – setzen und lesen und/oder direkt auf die Attribute zugreifen. Der Attributzugriff ist vergleichbar mit der Standardserialisierung, und der Zugriff über die Property ist von der JavaBeans Persistence über java. beans.XMLEncoder/java.beans.XMLDecoder realisiert. Welchen Weg JAXB gehen soll, bestimmt die Annotation @XmlAccessorType, die üblicherweise an der Klasse festgemacht wird. Drei Werte sind interessant:

Wert

Datenaustausch über

@XmlAccessorType(XmlAccessType.FIELD)

jedes nichtstatische Attribut

@XmlAccessorType(XmlAccessType.PROPERTY)

jede JavaBean-Property

@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)

jede öffentliche JavaBean-Property oder jedes öffentliche Attribut

Tabelle 8.5Womit JAXB die Daten austauscht

Die Standardbelegung ist AccessType.PUBLIC_MEMBER.

@Transient

Die Annotation @Transient nimmt ein Element von der XML-Abbildung aus. Das ist nützlich für den XmlAccessType.FIELD oder XmlAccessType.PROPERTY, da dann auch private Eigenschaften geschrieben werden, was allerdings nicht in jedem Fall erwünscht ist:

Java-Beispiel

XML-Ergebnis

class Person {
@XmlTransient public int id;
public String firstname;
public String lastname;
}
<person>
<firstname>Christian</firstname>
<lastname>Ullenboom</lastname>
</person>

Tabelle 8.6Beispiel für @Transient und Ergebnis

Werte als Attribute schreiben mit @XmlAttribute

Üblicherweise schreibt JAXB jeden Wert in ein eigenes XML-Element. Soll der Wert als Attribut geschrieben werden, kommt die Annotation @XmlAttribute zum Einsatz:

Java-Beispiel

XML-Ergebnis

class Person {
public String name;
public @XmlAttribute int id;
}
<person id="123">
<name>Christian</name>
</person>

Tabelle 8.7Beispiel für @XmlAttribute und Ergebnis

Reihenfolge der Elemente ändern

Ist die Reihenfolge der XML-Elemente wichtig, so lässt sich mit dem propOrder von @XmlType die Reihenfolge der Eigenschaften bestimmen:

Java-Beispiel

XML-Ergebnis

class Person {
public String lastname,
firstname;
}
<person>
<lastname>Ullenboom</lastname>
<firstname>Christian</firstname>
</person>
@XmlType(
propOrder = { "firstname", "lastname" }
)
class Person {
public String lastname, firstname;
}
<person>
<firstname>Christian</firstname>
<lastname>Ullenboom</lastname>
</person>

Tabelle 8.8XML-Abbildung ohne und mit @XmlType(propOrder…)

Einzelner Wert ohne eigenes XML-Element

Gibt es nur ein Element in der Klasse, so kann @XmlValue es direkt ohne Unterelement in den Rumpf setzen:

Java-Beispiel

XML-Ergebnis

class Person {
public int id;
}
<person>
<id>123</id>
</person>
class Person {
public @XmlValue int id;
}
<person>123</person>

Tabelle 8.9XML-Abbildung mit @XmlValue

Kompakte Listendarstellung

Die Datenstruktur Liste wird in JAXB üblicherweise so abgebildet, dass jedes Listenelement einzeln in ein XML-Element kommt. Die Annotation @XmlList weist JAXB an, Elemente einer Sammlung mit Leerzeichen zu trennen. Das funktioniert gut bei IDs, aber natürlich nicht mit allgemeinen Zeichenketten, die Leerzeichen enthalten:

Java-Beispiel

XML-Ergebnis

class Person {
public List<String> emails;
}
<person>
<emails>muh@kuh.de</emails>
<emails>zick@zak.com</emails>
</person>
class Person {
public @XmlList List<String> emails;
}
<person>
<emails>muh@kuh.de zick@zak.com</emails>
</person>

Tabelle 8.10Beispiel für @XmlList

Elemente zusätzlich einpacken

Die Annotation @XmlElementWrapper dient dazu, ein zusätzliches XML-Element zu erzeugen. In der Regel wird das für Sammlungen angewendet, wie auch das folgende Beispiel zeigt:

Java-Beispiel

XML-Ergebnis

class Person {
public List<String> emails;
}
<person>
<emails>muh@kuh.de</emails>
<emails>zick@zack.com</emails>
</person>
class Person {
@XmlElementWrapper(name = "emails")
@XmlElement(name = "email")
public List<String> emails;
}
<person>
<emails>
<email>muh@kuh.de</email>
<email>zick@zack.com</email>
</emails>
</person>

Tabelle 8.11Beispiel für @XmlElementWrapper/@XmlElement

Anpassen der XML-Abbildung

Nicht immer passt die Standardabbildung eines Datentyps gut. Für Farben sollen zum Beispiel nicht die Rot-, Grün- und Blau-Werte einzeln geschrieben werden, sondern alles kompakt in einem String. Auch die Standardabbildung für Datumswerte trifft nicht jeden Geschmack:

Java-Beispiel

XML-Ergebnis

class Person {
public Date birthday;
}
<person>
<birthday>1973-03-12T00:00:00+01:00</birthday>
</person>

Tabelle 8.12Standard-XML-Abbildung eines Datums

Für Aufgaben dieser Art erlaubt die Annotation @XmlJavaTypeAdapter die Angabe einer Konverterklasse, die einmal den Weg vom Objekt in eine String-Repräsentation für das XML-Element und dann vom String in das Objekt zurück beschreibt:

class Person {
@XmlJavaTypeAdapter( DateAdapter.class )
public Date birthday;
}

Die eigene Klasse DateAdapter erweitert die vorgegebene JAXB-Klasse XmlAdapter und überschreibt zwei Methoden für beide Konvertierungswege:

class DateAdapter extends XmlAdapter<String, Date> {
private final static DateFormat formatter = new SimpleDateFormat( "dd/MM/yyyy" );

public Date unmarshal( String date ) throws ParseException {
return formatter.parse( date );
}

public String marshal( Date date ) {
return formatter.format( date );
}
}

Damit bekommt die Ausgabe das gewünschte Format:

<person>
<birthday>12/03/1973</birthday>
</person>
abstract class javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType>
  • abstract ValueType marshal(BoundType v)
    Konvertiert v in einen Werttyp, der dann in eine XML-Repräsentation überführt wird.

  • abstract BoundType unmarshal(ValueType v)
    Überführt den Wert in den XML-Typ.

ValueType und BoundType sind Typvariablen, aber ungewöhnlicherweise sind es keine einfachen Großbuchstaben.

Der spezielle Datentyp XMLGregorianCalendar

Neben der Möglichkeit, Datumswerte mit einem @XmlJavaTypeAdapter/XmlAdapter zu übersetzen, bietet JAXB den speziellen abstrakten Datentyp XMLGregorianCalendar. Die Abbildung in XML ist kompakter:

Java-Beispiel

XML-Ergebnis

class Person {
public XMLGregorianCalendar birthday;
}
<person>
<birthday>1973-03-12</birthday>
</person>

Tabelle 8.13Standardabbildung von XMLGregorianCalendar

XMLGregorianCalendar wird auch automatisch von dem Werkzeug xjc genutzt, wenn in der XML-Schema-Datei ein Datum vorkommt. Nicht ganz einfach sind die Erzeugung eines XMLGregorianCalendar-Objekts und die Belegung – hier gibt es noch Potenzial für Verbesserungen:

Person p = new Person();
GregorianCalendar c = new GregorianCalendar( 1973, Calendar.MARCH, 12 );
XMLGregorianCalendar gc = DatatypeFactory.newInstance().newXMLGregorianCalendar( c );
gc.setTimezone( DatatypeConstants.FIELD_UNDEFINED );
gc.setTime( DatatypeConstants.FIELD_UNDEFINED,
DatatypeConstants.FIELD_UNDEFINED,
DatatypeConstants.FIELD_UNDEFINED );
p.birthday = gc;

Die abstrakte Klasse DatatypeFactory bietet weitere statische Methoden für Mapper-Objekte, die XML in Objekte überführen oder umgekehrt:

abstract class javax.xml.datatype.DatatypeFactory
  • static DatatypeFactory newInstance()
    Liefert eine DatatypeFactory-Implementierung.

  • abstract XMLGregorianCalendar newXMLGregorianCalendar()
    Liefert einen XMLGregorianCalendar, bei dem alle Werte undefiniert sind.

  • abstract XMLGregorianCalendar newXMLGregorianCalendar(GregorianCalendar cal)

  • XMLGregorianCalendar newXMLGregorianCalendarDate(int year, int month, int day, int timezone)

  • XMLGregorianCalendar newXMLGregorianCalendarTime(int hours, int minutes, int seconds,
    int timezone)

  • XMLGregorianCalendar newXMLGregorianCalendarTime(int hours, int minutes, int seconds,
    BigDecimal fractionalSecond, int timezone)

  • XMLGregorianCalendar newXMLGregorianCalendar(int year, int month, int day, int hour,
    int minute, int second, int millisecond, int timezone)

  • abstract XMLGregorianCalendar newXMLGregorianCalendar(BigInteger year, int month, int day,int hour, int minute, int second, BigDecimal fractionalSecond, int timezone)
    Liefert ein XMLGregorianCalendar-Objekt mit unterschiedlichen Vorbelegungen.

Weiterhin gibt es newDuration(…)-Methoden, die javax.xml.datatype.Duration-Objekte liefern. Die Duration-Objekte können auf XMLGregorianCalendar aufaddiert werden bzw. repräsentieren in XML-Schema-Dateien den Typ xs:duration.

[zB]Beispiel

Eine XML-Schema-Datei soll für das Element period eine Dauer definieren:

<xs:element name="period" type="xs:duration"/>

Angewendet kann es so aussehen, um die Dauer von einem Jahr und einem Monat anzugeben:

<period>P1Y1M</period>

Werden aus Schema-Dateien die JavaBeans automatisch generiert, wird Duration für xs:duration eingesetzt.

Hierarchien einsetzen

Die XML-Abbildung von Objekten, die in Klassenbeziehungen organisiert sind, bedarf einer besonderen Vorbereitung. Nehmen wir an, Player und Key sind zwei Klassen, die von GameObject abgeleitet sind (eine Schnittstelle wäre für JAXB auch möglich). Unser Ziel ist es, Spieler und Schlüssel in einen Raum zu setzen:

abstract class GameObject {
public String name;
}

@XmlRootElement public class Player extends GameObject { }

@XmlRootElement public class Key extends GameObject {
public int id;
}

Zunächst gilt, dass die konkreten Klassen die Annotation @XmlRootElement tragen müssen. Ein Beispielraum soll einen Spieler und einen Schlüssel beherbergen:

Player player= new Player();
player.name = "Chris";

Key key = new Key();
key.name = "Entretenimiento";
key.id = 12;

Room room = new Room();
room.objects.add( key );
room.objects.add( player );

Der Raum referenziert in einer Liste allgemeine Objekte vom Typ GameObject. Nun reicht im Room ein einfaches

public List<GameObject> objects = new ArrayList<GameObject>();

zum Halten der Objektverweise aber nicht aus! Beim Verarbeiten würde JAXB die Information fehlen, welches Element denn tatsächlich in der Liste ist, denn ein Player sollte ja etwa durch <player> beschrieben sein und ein Schlüssel durch <key>. Die Abbildung kann nicht <objects> lauten, denn beim Lesen muss ein konkreter Untertyp rekonstruiert werden; wenn JAXB beim Lesen ein <objects> sieht, weiß es erst einmal nicht, ob ein Player oder ein Key zu erzeugen und in die Liste zu hängen ist. Das Ziel ist aber die folgende Abbildung:

<room>
<key>
<name>Entretenimiento</name>
<id>12</id>
</key>
<player>
<name>Chris</name>
</player>
</room>

Die Lösung liegt in der Anwendung der Annotationen @XmlElementRefs und @XmlElementRef. Ersteres ist ein Container, und das Zweite bestimmt den Typ, der in der Liste zu erwarten ist:

@XmlRootElement public class Room {
@XmlElementRefs( {
@XmlElementRef( type = Player.class ),
@XmlElementRef( type = Key.class ),
} )
public List<GameObject> objects = new ArrayList<GameObject>();
}

Mit diesem Hinweis berücksichtigt JAXB den Typ der Kinder und schreibt nicht einfach <objects>. Die Elementtypen in der Sammlung sind von uns mit @XmlRootElement annotiert und geben den Namen der XML-Elemente »player« und »key« vor (wir hätten natürlich mit so etwas wie @XmlRootElement(name="sportsman") den XML-Elementnamen überschreiben können).

 
Zum Seitenanfang

8.4.7Beans aus XML-Schema-Datei generieren Zur vorigen ÜberschriftZur nächsten Überschrift

Da es für existierende XML-Dateien mühselig ist, die annotierten JavaBeans von Hand aufzubauen, gibt es einen Generator, genannt Java Architecture for XML Binding Compiler, kurz xjc.Er kann von der Kommandozeile, vom Ant-Skript oder auch von Entwicklungsumgebungen aus aufgerufen werden. Er nimmt eine XML-Schema-Datei und generiert die Java-Klassen und eine ObjectFactory, die als – wie der Name schon sagt – Fabrik für die gemappten Objekte aus den XML-Elementen fungiert.

xjc aufrufen

Im ersten Schritt wechseln wir auf die Kommandozeile und testen entweder, ob das bin-Verzeichnis vom JDK im Suchpfad ist, oder wir wechseln in das bin-Verzeichnis, sodass wir xjc direkt aufrufen können, und folgende Ausgabe erscheint:

$ xjc

Usage: xjc [-options ...] <schema file/URL/dir/jar> ... [-b <bindinfo>] ...
If dir is specified, all schema files in it will be compiled.
If jar is specified, /META-INF/sun-jaxb.episode binding file will be compiled.
Options:
-nv : do not perform strict validation of the input schema(s)
-extension : allow vendor extensions – do not strictly follow the
Compatibility Rules and App E.2 from the JAXB Spec
-b <file/dir> : specify external bindings files (each <file> must have its own -b)
If a directory is given, **/*.xjb is searched
-d <dir> : generated files will go into this directory
-p <pkg> : specifies the target package
-httpproxy <proxy> : set HTTP/HTTPS proxy. Format is [user[:password]@]
proxyHost:proxyPort
-httpproxyfile <f> : Works like -httpproxy but takes the argument in a file to
protect password
-classpath <arg> : specify where to find user class files
-catalog <file> : specify catalog files to resolve external entity references
support TR9401, XCatalog, and OASIS XML Catalog format.
-readOnly : generated files will be in read-only mode
-npa : suppress generation of package level annotations
(**/package-info.java)
-no-header : suppress generation of a file header with timestamp
-target (2.0|2.1) : behave like XJC 2.0 or 2.1 and generate code that doesnt use
any 2.2 features.
-enableIntrospection : enable correct generation of Boolean getters/setters to
enable Bean Introspection apis
-contentForWildcard : generates content property for types with multiple
xs:any derived elements
-xmlschema : treat input as W3C XML Schema (default)
-relaxng : treat input as RELAX NG (experimental,unsupported)
-relaxng-compact : treat input as RELAX NG compact syntax (experimental,unsupported)
-dtd : treat input as XML DTD (experimental,unsupported)
-wsdl : treat input as WSDL and compile schemas inside it
(experimental,unsupported)
-verbose : be extra verbose
-quiet : suppress compiler output
-help : display this help message
-version : display version information
-fullversion : display full version information

Extensions:
-Xinject-code : inject specified Java code fragments into the generated code
-Xlocator : enable source location support for generated code
-Xsync-methods : generate accessor methods with the 'synchronized' keyword
-mark-generated : mark the generated code as @javax.annotation.Generated
-episode <FILE> : generate the episode file for separate compilation

Eigentlich ist bis auf die Angabe der Schema-Quelle (aus einer Datei oder alternativ die URL) keine weitere Angabe nötig. Es ist aber praktisch, zwei Optionen zu setzen: -p bestimmt das Java-Paket für die generierten Klassen und -d das Ausgabeverzeichnis, in dem der Generator die erzeugten Dateien ablegt.

Wetterdaten von Open Weather Map beziehen

Für ein Beispiel wählen wir einen Dienst der Open Weather Map, eines frei zugänglichen Servers für Wetterdaten. Die Webseite http://openweathermap.org/API zeigt den Einsatz der einfachen API, bei der ganz simpel in der URL ein Ort kodiert ist, von dem die Wetterdaten erfragt werden sollen, etwa so: http://api.openweathermap.org/data/2.5/find?q=Frankfurt&units=metric& mode=xml. Alternativ lassen sich Grafiken aufbauen, nach Geo-Koordinaten suchen oder statt XML auch JSON produzieren – für unser Beispiel ist XML ganz gut, das für die Anfrage wie folgt aussieht:

<?xml version="1.0" encoding="utf-8"?>
<cities>
<calctime>0.0027</calctime>
<count>1</count>
<mode>name</mode>
<list>
<item>
<city id="2925533" name="Frankfurt am Main">
<coord lon="8.68341" lat="50.1121"/>
<country>Germany</country>
<sun rise="2014-03-13T05:42:01" set="2014-03-13T17:27:13"/>
</city>
<temperature value="15.25" min="13.2" max="17" unit="celsius"/>
<humidity value="38" unit="%"/>
<pressure value="1026" unit="hPa"/>
<wind>
<speed value="0.5" name="Calm"/>
<direction value="60" code="ENE" name="East-northeast"/>
</wind>
<clouds value="0" name="sky is clear"/>
<precipitation mode="no"/>
<weather number="800" value="Sky is Clear" icon="01n"/>
<lastupdate value="2014-03-13T17:56:36" unix="1394733396"/>
</item>
</list>
</cities>

Für unser Beispiel wollen wir das XML-Dokument nicht von Hand auseinanderpflücken, sondern JAXB soll uns eine gefüllte JavaBean mit allen Informationen liefern. Leider gibt es für diese einfache XML-Datei kein XML-Schema, sodass wir uns mit einem Trick behelfen, nämlich aus einer XML-Datei ein Schema generieren lassen und dann mit diesem zu xjc gehen. Es gibt im Internet einige freie XML-nach-Schema-Konverter, für mein Beispiel nutze ich http://www. xmlforasp.net/CodeBank/System_Xml_Schema/BuildSchema/BuildXMLSchema.aspx. Damit die Datentypen korrekt erkannt werden, ändere ich in dem Beispiel die Windrichtung vom Ganzzahltyp <direction value="60"…> auf eine Fließkommazahl, etwa value="60.123", sonst steht im generierten Schema der Typ xsd:int, was später zu Problemen führt, wenn der Dienst eine Fließkommazahl liefert.

<?xml version="1.0" encoding="utf-16"?>
<xsd:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="cities">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="calctime" type="xsd:decimal" />
<xsd:element name="count" type="xsd:int" />
<xsd:element name="mode" type="xsd:string" />
<xsd:element name="list">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="item">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="city">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="coord">
<xsd:complexType>
<xsd:attribute name="lon" type="xsd:decimal" />
<xsd:attribute name="lat" type="xsd:decimal" />
</xsd:complexType>
</xsd:element>
<xsd:element name="country" type="xsd:string" />
<xsd:element name="sun">
<xsd:complexType>
<xsd:attribute name="rise" type="xsd:dateTime" />
<xsd:attribute name="set" type="xsd:dateTime" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:int" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="temperature">
<xsd:complexType>
<xsd:attribute name="value" type="xsd:decimal" />
<xsd:attribute name="min" type="xsd:decimal" />
<xsd:attribute name="max" type="xsd:int" />
<xsd:attribute name="unit" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="humidity">
<xsd:complexType>
<xsd:attribute name="value" type="xsd:int" />
<xsd:attribute name="unit" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="pressure">
<xsd:complexType>
<xsd:attribute name="value" type="xsd:int" />
<xsd:attribute name="unit" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="wind">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="speed">
<xsd:complexType>
<xsd:attribute name="value" type="xsd:decimal" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="direction">
<xsd:complexType>
<xsd:attribute name="value" type="xsd:decimal" />
<xsd:attribute name="code" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="clouds">
<xsd:complexType>
<xsd:attribute name="value" type="xsd:int" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="precipitation">
<xsd:complexType>
<xsd:attribute name="mode" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="weather">
<xsd:complexType>
<xsd:attribute name="number" type="xsd:int" />
<xsd:attribute name="value" type="xsd:string" />
<xsd:attribute name="icon" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="lastupdate">
<xsd:complexType>
<xsd:attribute name="value" type="xsd:dateTime" />
<xsd:attribute name="unix" type="xsd:int" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>

Diese Ausgabe sollte in einer Datei gespeichert werden, damit sie von xjc verwendet werden kann; nennen wir diese Datei etwa weather.xsd.

In Eclipse lässt sich xjc einfach aufrufen. Selektiere im Package-Explorer weater.xsd, im Kontextmenü Generate • JAXB Classes…, dann wähle das Java-Projekt aus, anschließend Next. Im folgenden Dialog gib als Paket »com.tutego.insel.xml.jaxb.weather« ein, dann klicke auf Finish. Wichtig! Damit xjc gefunden wird, muss das JDK aktiviert sein, nicht das JRE; nur das JDK bringt das Werkzeug xjc mit.

Ein Aufruf von der Kommandozeile sieht so aus (Ausgabe wie sie Eclipse liefert):

$ xjc -p com.tutego.insel.xml.jaxb.weather weather.xsd
Ein Schema wird geparst ...
Ein Schema wird kompiliert ...
com\tutego\insel\xml\jaxb\weather\Cities.java
com\tutego\insel\xml\jaxb\weather\ObjectFactory.java

Das Tool generiert alle Typen für den komplexen Typ aus dem XML-Schema, um eine Paket-Annotation festmachen zu können, und ObjectFactory, die einfache Fabrikmethoden enthält, um die gemappten Objekte aufbauen zu können.

Dann kann ein Java-Programm den Service mit einer URL ansprechen und sich das Ergebnis-XML in eine JavaBean konvertieren lassen.

Listing 8.18com/tutego/insel/xml/jaxb/Weather.java, main()

String url = "http://api.openweathermap.org/data/2.5/find?q=Frankfurt&units=metric&mode=xml";
Cities city = JAXB.unmarshal( url, Cities.class );
System.out.printf( "%.1f° C%n", city.getList().getItem().getTemperature().getValue() );
// z.B. 15,1° C

Konflikte in der Schema-Datei *

Leider haben viele XML-Schemas ein Problem, sodass sie nicht direkt vom Schema-Compiler verarbeitet werden können. Ein Beispiel zeigt das Dilemma:

<container>
<head><content title="Titel"/></head>
<body><content doc="doc.txt"/></body>
</container>

In der hierarchischen Struktur heißt das in <head> und <body> vorkommende XML-Element gleich, nämlich content. Die Schema-Datei kann widerspruchslos definieren, dass die beiden XML-Elemente gleich heißen, aber unterschiedliche Attribute erlauben, sozusagen das Head-Content und das Body-Content. Allein durch ihre Hierarchie, also dadurch, dass sie einmal unter head und einmal unter body liegen, sind sie eindeutig bestimmt. Der Schema-Compiler von Java bekommt aber Probleme, da er diese hierarchische Information in eine flache bringt. Er kann einfach eine Klasse Head und Body aufbauen, aber bei <content> steht er vor einem Problem. Da die Schema-Definitionen unterschiedlich sind, müssten zwei verschiedene Java-Klassen unter dem gleichen Namen Content generiert werden. Das geht nicht, und xjc bricht mit Fehlern ab.

Fehler dieser Art gibt es leider häufig, und sie sind der Grund, warum aus vielen Schemas nicht einfach JavaBeans generiert werden können. Erfolglos ohne weitere Einstellungen sind beispielsweise DocBook, Office Open XML, SVG, MathML und weitere. Doch was könnte die Lösung sein? xjc sieht Konfigurationsdateien vor, die das Mapping anpassen können. In diesen Mapping-Dokumenten identifiziert ein XPath-Ausdruck die problematische Stelle und gibt einen Substitutionstyp an. Die Dokumentation auf der JAXB-Homepage weist Interessierte in die richtige Richtung.

JAXB-Plugins

Auf der Webseite http://java.net/projects/jaxb2-commons/ gibt es eine Reihe nützlicher zusätzlicher Plugins für JAXB. Zu ihnen gehören:

  • JAXB2 Basic Plugins: Diese Plugin-Sammlung besteht aus Equals (erzeugt die equals(…)-Methode, wobei der Gleichheitstest über ein Equals-Strategie-Objekt austauschbar ist), HashCode (erzeugt hashCode(), wobei auch hier die Berechnung des Hashwerts über ein Strategie-Objekt erfolgt), ToString (erzeugt toString(), auch wieder über ein externes ToStringStrategy-Objekt), Copyable (erzeugt copyTo(…), um Objektzustände in ein anderes typkompatibles Objekt zu kopieren, und auch clone(); arbeitet dabei mit CopyStrategie), Mergeable (realisiert mergeFrom(…)-Methoden mit MergeStrategy-Objekten, die Zustände eines anderen Objekts mit den eigenen vereinen), JAXBIndex (generiert eine jaxb.index-Datei, die alle generierten Schema-Klassen auflistet), Inheritance und AutoInheritance (erlauben JAXB-Beans, globale Elemente oder komplexe Typen von gewünschten Basisklassen zu erben oder Schnittstellen zu implementieren), Wildcard (spezifiziert, wie sich das Wildcard-Property verhalten soll), Setters (etwas andere Setter-Implementierung bei Sammlungen) und Simplify Plugin (komplexe Properties in Einzel-Properties aufspalten).

  • Camelcase Always Plugin: Sind die Elementnamen groß geschrieben, so wird JAXB automatisch groß geschriebene Properties umsetzen, sodass etwa aus NAME der Setter/Getter setNAME(…) und getNAME() entsteht. Das Plugin verhindert dies und nennt die Setter/Getter wie gewohnt setName(…) und getName().

  • Value-Constructor Plugin: Jede JavaBean bekommt von xjc nur einen Standard-Konstruktor. Das Plugin gibt einen weiteren Konstruktor hinzu, der alle Attribute direkt initialisiert.

  • Default Value Plugin: Ein XML-Schema kann mit defaultValue vordefinierte Initialbelegungen für Attribute angeben. xjc ignoriert diese. Das Plugin wertet diese Vorbelegungen aus und initialisiert die Attribute der JavaBean gemäß den Werten.

  • Property Change Listener Injector Plugin: Eine über xjc generierte JavaBean schreibt einen bei setXXX(…) übergebenen Wert direkt in das private Attribut durch. Mit dem Plugin wird ein VetoableChangeListener eingeführt, der gegen Wertänderungen votieren kann.

  • Fluent API Plugin: Anstatt eine Bean nur mit Setter-Aufrufen zu initialisieren, etwa eine Person mit setName(…) und setAge(…), generiert das Fluent API Plugin kaskadierbare Methoden, sodass im Beispiel unserer Person nur new Person().withName(…).withAge(…) zu schreiben ist.

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java SE 8 Standard-Bibliothek Java SE 8 Standard-Bibliothek
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Java ist auch eine Insel
Java ist auch eine Insel


Zum Rheinwerk-Shop: Professionell entwickeln mit Java EE 8
Professionell entwickeln mit Java EE 8


Zum Rheinwerk-Shop: Besser coden
Besser coden


Zum Rheinwerk-Shop: Entwurfsmuster
Entwurfsmuster


Zum Rheinwerk-Shop: IT-Projektmanagement
IT-Projektmanagement


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2018
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 
Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern