JAXB ist eine API zum Übertragen von Objektzuständen auf XML-Dokumente und umgedreht. 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 setzten können, aber JavaBeans werden gleich zusammen mit den Annotationen von einem Werkzeug aus deiner XML-Schema-Datei generiert.
Java 6 integriert JAXB 2.0, und das JDK 6 Update 4 – sehr ungewöhnlich für ein Update – aktualisiert auf JAXB 2.1.
1.1.1 Bean für JAXB aufbauen
Wir wollen einen Player deklarieren, und JAXB soll ihn anschließend in ein XML-Dokument übertragen.
com/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.
1.1.2 JAXBContext und die Marshaller
Ein kleines Testprogramm baut eine Person auf bildet sie dann in XML ab – die Ausgabe der Abbildung kommt auf dem Bildschirm.
com/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 );
Nach dem Lauf kommt auf den Schirm:
<?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() erwartet standardmäßig eine Aufzählung der Klassen, die JAXB behandeln soll. Der JAXBContext erzeugt den Marshaller zum Schreiben und Unmarshaller zum Lesen. Die Fabrikmethode createMarshaller() liefert einen Schreiberling, der mit marshal() das Wurzelobjekt in einen Datenstrom schreibt. Das zweite Argument von marshal() ist unter anderem ein OutputStream (wie System.out in unserem Beispiel), Writer oder File-Objekt.
JAXB beachtet standardmäßig alle Bean-Properties, also birthday und name, und nennt die XML-Elemente nach den Properties.
1.1.3 Ganze Objektgrafen schreiben und lesen
JAXB bildet nicht nur das zu schreiben 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 kleine Player als Wurzelobjekte zum Marshaller kommen.
com/tutego/insel/xml/xml/jaxb/Room.java, Room
@XmlRootElement( namespace = „http://tutego.com/“ )
public class Room
{
private List<Player> players = new ArrayList<Player>();
@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 Objektgrafen 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 2 Spielern aufbaut, und diesen Raum dann in eine XML-Datei schreibt. Statt allerdings JAXBContext direkt zu nutzen und einen Marshaller zum Schreiben und Unmarshaller zum Lesen zu erfragen, kommt im zweiten Beispiel die Utility-Klasse JAXB zum Einsatz, die ausschließlich statische überladene marshal() und unmarshal() Methoden anbietet.
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 ) );
File file = File.createTempFile( „room-jaxb-„, „.xml“ );
JAXB.marshal( room, file );
Room room2 = JAXB.unmarshal( file, Room.class );
System.out.println( room2.getPlayers().get( 0 ).getName() ); // John Peel
file.deleteOnExit();
Falls etwas beim Schreiben oder Lesen misslingt, werden die vorher geprüften Ausnahmen in einer DataBindingException ummantelt, die eine RuntimeException ist.
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.
Schön von Galileo Openbook kopiert.
Schon frech, dass Christian Ullenboom (ich) von Christian Ullenboom (mir) klaut.
PS: Alle Kapitel des Buches veröffentliche ich erst hier im Blog, bis sie später ins Buch kommen. Im Buch ist also immer die aktuelle Version zu finden, auch rechtschreibkorrigiert. Hier im Blog können meiner Leser über die neuen Buchabschnitte diskutieren, mich auf inhaltliche/didaktische Verständnisprobleme hinweisen, bevor es „zu spät“ ist und der Text in Papierform materialisiert ist.