Castor XML

 

Was ist Castor?

 

Castor XML ist ein mächtiges Werkzeug zur Bindung von Java Objekten und XML Dokumenten. Das heisst, man kann ohne grossen Aufwand XML Dokumente in Java Objekte umwandeln, und umgekehrt. Beachten Sie, dass Castor weitaus mehr Funktionen anbietet. So zum Beispiel auch ein Java/SQL Binding. Wir wollen uns in diesem Artikel jedoch nur um die XML Problematik kümmern.

Was machen wir damit?

 

Stellen wir uns vor, wir hätten ein einfaches XML Dokument, welches wir in unserem Java Projekt bearbeiten wollen. Nun ist es doch recht mühsam und unschön, das ganze Dokument mit den XML APIs SAX oder DOM zu parsen. Wäre es nicht viel schöner, wir könnten das XML Dokument wie ein ganz normales Java Objekt verwenden und nach getaner Arbeit wieder in ein XML Dokument speichern? Wenn Sie diese Frage mit „Ja“ beantworten würden, sollten Sie weiter lesen. Schauen Sie sich das folgende Beispiel an, um ein Gefühl dafür zu bekommen, wie man mit Castor arbeitet. Lassen Sie sich nicht verwirren, später wird alles genauer erklärt!

 

Unsere XML Struktur:

 

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

<AdressBuch>

       <AdressEintrag>

             <name>Meier</name>    

             <vorname>Josef</vorname>

             <alter>12</alter>

       </AdressEintrag>    

       <AdressEintrag>

             <name>Muster</name>

             <vorname>Max</vorname>

             <alter>99</alter>

       </AdressEintrag>

</AdressBuch>

 

 

Unser Java Code:

 
// das Objekt laden
reader = new FileReader(„beispiel.xml“);
AdressBuch ab = (AdressBuch)Unmarshaller.unmarshal(AdressBuch.class, reader);
 
// .. hier können wir das Objekt bearbeiten
 
// das Objekt wieder speichern
writer = new FileWriter("beispiel.xml");
Marshaller.marshal(person, writer);

Was brauchen wir?

 

Schalten wir einen Gang zurück und sehen wir uns einmal an, was Sie für den erfolgreichen Einsatz von Castor benötigen.

 

Zuallererst benötigen Sie natürlich die Castor API. Diese können Sie auf http://www.castor.org herunterladen. Zusätzlich brauchen Sie noch den Xerces XML Parser von http://xml.apache.org/xerces2-j/.

 

Grundsätzliche Kenntnisse über XML und XSD werden in diesem Artikel vorausgesetzt. Da die Beispiele jedoch sehr einfach gehalten sind, sollten sie auch für Laien leicht verständlich sein.

Ein einführendes Beispiel

 

Es gibt zwei grundsätzliche Ansätze, mit Castor zu arbeiten:

 

*      Man kennt die XML Struktur und möchte nun Castor verwenden, um die XML Daten als Java Objekte zu bearbeiten

*      Man hat ein Java Datenmodell und möchte die Objekte nun in XML Dokumente umwandeln.


Wir kennen die XML Struktur

 

Dummerweise nutzt uns die XML Struktur alleine noch nicht viel, wir brauchen ja Java Objekte. Diese können wir über den Source Code Generator bequem erstellen lassen. Dazu benötigen wir jedoch noch eine XML Schema Definition unserer XML Struktur.

 

Unsere XML Struktur (beispiel.xml):

 

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

<Person>

       <vorname>Max</vorname>

       <name>Muster</name>

       <alter>99</alter>

</Person>

 

Das dazu passende XML Schema (beispiel.xsd):

 

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

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">

       <xs:element name="Person">

             <xs:complexType>

                    <xs:sequence>

                           <xs:element ref="vorname"/>

                           <xs:element ref="name"/>

                           <xs:element ref="alter"/>

                    </xs:sequence>

             </xs:complexType>

       </xs:element>

       <xs:element name="alter" type="xs:integer"/>

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

       <xs:element name="vorname" type="xs:string"/>

</xs:schema>

 

Anhand dieser Informationen können wir nun einfach unsere Java Objekte generieren lassen. Stellen Sie sicher, dass sich alle Bibliotheken in Ihrem Klassenpfad befinden!

 

java org.exolab.castor.builder.SourceGenerator -i beispiel.xsdpackage ch.menzsoft.castorsample

 

Der Generator erzeugt anhand des XSDs beispiel.xsd Java Klassen, welche das Datenmodell repräsentieren. Zusätzlich erstellt der Generator auch Klassendeskriptor Klassen, welche Informationen über das Datenmodell bereithalten. Diese Deskriptoren werden von Castor verwendet und interessieren uns im Moment nicht besonders.

 

Wenn alles geklappt hat, sollten Sie nun im Verzeichnis ch/menzsoft/castorsample zwei Klassen vorfinden:

 

*      Person.java

*      PersonDescriptor.java

 

Nun haben wir alles war wir brauchen, um ein einfaches Beispielprogramm zu entwickeln.


Na dann, legen wir los! Sie werden sehen, es ist ganz einfach, mit Castor zu arbeiten.

 

package ch.menzsoft.castorsample;

 

import java.io.FileReader;

import java.io.IOException;

 

import org.exolab.castor.xml.MarshalException;

import org.exolab.castor.xml.Unmarshaller;

import org.exolab.castor.xml.ValidationException;

 

public class Beispiel {

 

    public static void main(String[] args) {

        try {

            FileReader reader = new FileReader("beispiel.xml");

            Object obj = Unmarshaller.unmarshal(Person.class, reader);

            Person person = (Person) obj;

            System.out.println(person.getName());

            System.out.println(person.getVorname());

            System.out.println(person.getAlter());

        } catch (IOException e) {

            e.printStackTrace();

        } catch (MarshalException e) {

            e.printStackTrace();

        } catch (ValidationException e) {

            e.printStackTrace();

        }

    }

}

 

Das Programm generiert (hoffentlich) den folgenden Output:

 

Muster

Max

99

 

In diesem Beispiel haben wir das Objekt aus einem XML Dokument erzeugt. Wir können das Objekt nun bearbeiten und wieder speichern.

 

// das Objekt laden

FileReader reader = new FileReader("beispiel.xml");

Object obj = Unmarshaller.unmarshal(Person.class, reader);

Person person = (Person) obj;

 

// bearbeiten

person.setAlter(12);

 

// und wider speichern

FileWriter writer = new FileWriter("beispiel.xml");

Marshaller.marshal(person, writer);

writer.close();

 

Wenn Sie sich die Datei beispiel.xml nun anschauen, werden Sie feststellen, dass unser Max Muster um ein paar Jahre jünger geworden ist. Toll nicht?


Wir haben bereits ein Datenmodell

 

Nehmen wir an, Ihr Datenmodell liegt bereits als eine Sammlung von Java Klassen vor und Sie möchten nun diese Java Objekte in XML Dokumenten ablegen. Auch das geht mit Castor möglich.

 

Für unser Problem stellt Castor zwei Möglichkeiten zur Verfügung.

 

*      Wir lassen Castor alles selber machen

*      Wir sagen Castor, wie die Objekte in die XML Struktur gebracht werden müssen.

 

Am einfachsten ist es natürlich, wenn wir Castor einfach einmal machen lassen. Wir übergeben unser Objekt und Castor versucht nun, dieses in eine anständige XML Struktur zu bringen. Castor bedient sich dabei der Java Reflection API. Dazu muss für jedes Feld eine geeignete Getter/Setter Methode bereitstehen.

 

Das Haus:

 

package ch.menzsoft.castorsample;

 

public class Haus {

 

    private Dach dach;

 

    private Zimmer[] zimmer;

 

    public Haus() {

        // ein leerer Konstruktor ist ein absolutes Muss!

    }

 

    public void setDach(Dach dach) {

        this.dach = dach;

    }

 

    public Dach getDach() {

        return dach;

    }

 

    public void setZimmer(Zimmer[] zimmer) {

        this.zimmer = zimmer;

    }

 

    public Zimmer[] getZimmer() {

        return zimmer;

    }

 

}


Das Dach:

 

package ch.menzsoft.castorsample;

 

public class Dach {

 

    private int hoehe;

 

    public Dach() {

    }

   

    public Dach(int hoehe) {

        this.hoehe = hoehe;

    }

 

    public void setHoehe(int hoehe) {

        this.hoehe = hoehe;

    }

 

    public int getHoehe() {

        return hoehe;

    }

 

}

 

Das Zimmer:

 

package ch.menzsoft.castorsample;

 

public class Zimmer {

 

    private int laenge;

 

    private int breite;

 

    public Zimmer() {

    }

   

    public Zimmer(int laenge, int breite) {

        this.laenge = laenge;

        this.breite = breite;       

    }

 

    public void setLaenge(int laenge) {

        this.laenge = laenge;

    }

 

    public int getLaenge() {

        return laenge;

    }

 

    public void setBreite(int breite) {

        this.breite = breite;

    }

 

    public int getBreite() {

        return breite;

    }

 

}

 

Nun, das ist ein etwas komplexeres Beispiel. Wir haben ein Haus, bestehend aus mehreren Zimmern und einem Dach. Über Sinn oder Unsinn lässt sich wunderbar streiten. Und so einfach lässt sich unsere Objektstruktur speichern:

 

package ch.menzsoft.castorsample;

 

import java.io.FileWriter;

import java.io.IOException;

 

import org.exolab.castor.xml.MarshalException;

import org.exolab.castor.xml.Marshaller;

import org.exolab.castor.xml.ValidationException;

 

public class Beispiel2 {

 

    public static void main(String[] args) {

        // Objekt erzeugen

        Zimmer[] zimmer = { new Zimmer(5, 5), new Zimmer(10, 10)};

        Dach dach = new Dach(3);

        Haus haus = new Haus();

        haus.setDach(dach);

        haus.setZimmer(zimmer);

 

        // unser Castor Code

        try {

            FileWriter writer = new FileWriter("beispiel2.xml");

            Marshaller.marshal(haus, writer);

            writer.close();        

        } catch (IOException e) {

            e.printStackTrace();

        } catch (MarshalException e) {

            e.printStackTrace();

        } catch (ValidationException e) {

            e.printStackTrace();

        }

    }

 

}

 

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

<haus xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="java:ch.menzsoft.castorsample.Haus">

       <zimmer laenge="5" breite="5"/>

       <zimmer laenge="10" breite="10"/>

       <dach hoehe="3"/>

</haus>

 

Entspricht die Struktur Ihren Wünschen? Wenn ja, dann hat Castor gute Arbeit geleistet. In den meisten Fällen werden Sie aber nicht wirklich glücklich sein. Auch in diesem Fall wäre eine schönere Struktur wünschenswert. So etwas wie:

 

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

<haus xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="java:ch.menzsoft.castorsample.Haus">

       <zimmer>

             <zimmer l="5" b="5"></zimmer>

             <zimmer l="10" b="10"></zimmer>            

       </zimmer>

       <dach h="3"/>

</haus>

 

Doch wie können wir das erreichen? Dazu hält Castor mittels Mapping Dateien eine recht flexible Lösung bereit. Diese Mapping Datei kann man als eine Art Brücke zwischen Java Objekten und XML Struktur verstehen. Und so funktioniert es:

 

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

<mapping>

       <description>Ein einfaches Beispiel</description>

       <class name="ch.menzsoft.castorsample.Zimmer">

             <map-to xml="zimmer"/>

             <field name="breite" type="int">

                    <bind-xml name="b" node="attribute"/>

             </field>

             <field name="laenge" type="int">

                    <bind-xml name="l" node="attribute"/>

             </field>

       </class>

       <class name="ch.menzsoft.castorsample.Dach">

             <map-to xml="dach"/>

             <field name="hoehe" type="int">

                    <bind-xml name="h" node="attribute"/>

             </field>

       </class>

       <class name="ch.menzsoft.castorsample.Haus">

             <map-to xml="haus"/>

             <field name="zimmer" type="ch.menzsoft.castorsample.Zimmer" collection="array">

                    <bind-xml name="zimmer" node="element"/>

             </field>

             <field name="dach" type="ch.menzsoft.castorsample.Dach">

                    <bind-xml name="dach" node="element"/>

             </field>

       </class>

</mapping>

 

Die Mapping Datei mag etwas kompliziert erscheinen, doch bei näherer Betrachtung sollten sich einige Dinge selbst erklären. Die Mapping Datei ist von der Struktur recht einfach aufgebaut: Für jede Java Klasse wird definiert, wie die Datenfelder in die XML Struktur abgebildet werden sollen.

 

<class name="ch.menzsoft.castorsample.Zimmer"> <!—Die Klasse à

             <map-to xml="zimmer"/> <!—In der XML Struktur soll die Klasse „zimmerheissen à

             <field name="breite" type="int">

                    <bind-xml name="b" node="attribute"/> <!—Das Feld breite soll als “b” gespeichert warden, und zwar als XML Attribut à

             </field>

             <field name="laenge" type="int">

                    <bind-xml name="l" node="attribute"/>

             </field>

       </class>

 

Konsultieren Sie bitte http://castor.exolab.org/xml-mapping.html für eine genaue Erklärung. Beachten Sie bitte, dass Sie nun einen eigenen Marshaller erstellen müssen und Sie nicht mehr die statischen Methoden der Marshaller Klasse verwenden können. Das Mapping muss ja irgendwie zugewiesen werden.

 

// Objekt erzeugen

Zimmer[] zimmer = { new Zimmer(5, 5), new Zimmer(10, 10)};

Dach dach = new Dach(3);

Haus haus = new Haus();

haus.setDach(dach);

haus.setZimmer(zimmer);

 

// unser Castor Code

try {

    FileWriter writer = new FileWriter("beispiel3.xml");

    Marshaller marshaller = new Marshaller(writer);

    Mapping mapping = new Mapping();

    mapping.loadMapping("mapping.xml");

    marshaller.setMapping(mapping);

    marshaller.marshal(haus);

    writer.close();        

} catch (IOException e) {

    e.printStackTrace();

} catch (MarshalException e) {

    e.printStackTrace();

} catch (ValidationException e) {

    e.printStackTrace();

} catch (MappingException e) {

    e.printStackTrace();

}       


Das bearbeiten und wieder speichern der Objekte geschieht analog. Auch hier müssen Sie einen eigenen Unmarshaller erzeugen:

 

Unmarshaller unmarshaller = new Unmarshaller(mapping);

FileReader reader = new FileReader("beispiel3.xml");

Haus haus = (Haus) unmarshaller.unmarshal(reader);

 

Fazit

 

Nach dieser kurzen Einführung sollten Sie wissen, was Castor ist und wofür es eingesetzt werden kann. Leider konnten nicht alle Aspekte und Features angesprochen werden. Es gibt wie bei jeder Technologie eine Lernkurve und sobald Sie komplexere Probleme mit Castor lösen möchten, werden Sie nicht darum herumkommen, tiefer in die Materie einzutauchen. Als Einstiegspunkt eignet sich http://castor.exolab.org/ hervorragend.

 

© Christian Menz