Mittwoch, 28. Juli 2010

JAXB und das @XmlRootElement

JAXB wird verwendet um aus einem XML Schema Java Klassen zu erstellen. Diese können dann mit Daten gefüllt und als XML - Datei abgespeichert werden. Andersrum können auch bestehende XML - Dateien in Java Klassen umgewandelt werden.
Je nach Schemadefinition ergibt sich dabei allerdings ein Problem.

Der folgende Ausschnitt beschreibt ein einfaches Schema:

<xs:schema targetnamespace="http://test.kirchner.de/jaxb"            xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="start">
        <xs:complextype>
       </xs:complextype>
    </xs:element>
</xs:schema>


JAXB erstellt daraus diese Klasse, die im Package test.kirchner.de.jaxb untergebracht wird:

@XmlRootElement(name = "start")
public class Start {
}

Das folgende Schema beschreibt dieselbe XML Struktur, nur das dem start Element jetzt ein Typ über das type Attribut zugeordnet wird und dieser nicht innerhalb des Elements deklariert wurde:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"            xmlns:mk="http://test.kirchner.de/jaxb"            targetNamespace="http://test.kirchner.de/jaxb">
   <xs:element name="start" type="mk:startType"/>
   <xs:complexType name="startType"/>
</xs:schema>

Hieraus wird von JABX jedoch folgendes erstellt:

@XmlType(name = "startType")
public class StartType {
}

In der zugehörigen ObjectFactory wird zum Erzeugen eines start Elements die folgende Methode zur Verfügung gestellt:

@XmlRegistry
public class ObjectFactory {
  private final static QName _Start_QNAME =
      new QName("http://test.kirchner.de/jaxb", "start");
  ...
  @XmlElementDecl(namespace = "http://test.kirchner.de/jaxb",
      name = "start")
  public JAXBElement createStart(StartType value) {
    return new JAXBElement(_Start_QNAME, StartType.class, null,         value);
  }
}

Daraus ergibt sich nun aber das Problem, dass kein XmlRootElement definiert ist und der Versuch die Java Klassen mit dem Marshaller als XML Datei zu speichern schlägt fehl. Die hervorgehobene Zeile führt zu einer javax.xml.bind.MarshalException

JAXBElement list = (new ObjectFactory()).createStart(new StartType());
StartType root = list.getValue();
JAXBContext jaxbContext = JAXBContext.newInstance("test.kirchner.de.jaxb");
Marshaller m = jaxbContext.createMarshaller();
m.marshal(root, new File("d:/start.xml"));

Ausschnitt aus dem Stacktrace:

#javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.SAXException2: unable to marshal type "test.kirchner.de.jaxb.StartType" as an element because it is missing an @XmlRootElement annotation]

Abhilfe schaffen lässt sich, indem man im Schema folgendes hinzufügt:

<jaxb:globalBindings>
   <xjc:simple/>
</jaxb:globalBindings>


Das komplette XSD sieht somit wie folgt aus:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mk="http://test.kirchner.de/jaxb" targetNamespace="http://test.kirchner.de/jaxb" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="1.0" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" jaxb:extensionBindingPrefixes="xjc">
    <xs:annotation>
        <xs:appinfo>
            <jaxb:globalBindings>
                <xjc:simple/>
            </jaxb:globalBindings>
        </xs:appinfo>
    </xs:annotation>
    <xs:element name="start" type="mk:startType"/>
    <xs:complexType name="startType"/>
</xs:schema>


Durch die xjc:simple wird von JAXB wieder die Start Klasse mit der @XmlRootElement Notation erzeugt.

Siehe auch: den Blog von Kohsuke Kawaguchi

Keine Kommentare:

Kommentar veröffentlichen