Advanced   Java   Services JAXB Back Next Up Home


Einführung

JAXB ist die Abkürzung für Java Architecture for XML Binding Spezielle Annotations aus dem Package javax.xml.bind.annotation ermöglichen ein (relativ) einfaches Mapping eines Javaobjekts (POJO) in eine Xml-Datei und umgekehrt. Man nennt das auch Marshalling und Unmarshalling.

Damit der Parser von JAXB richtig arbeiten kann, muß man den Quellcode einer Klasse mit passenden Annotations versehen.


Ein einfaches Beispiel

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlAttribute;

@XmlRootElement
public class Person
{
   private String vorname;
   private String nachname;
   private String profession;

   /* Defaultkonstruktor notwendig */
   public Person()
   {}

   /* Ein Konstruktor ist kein XmlElement (keine Property) */
   public Person(String vorname, String nachname, String profession)
   {
      this.vorname = vorname;
      this.nachname = nachname;
      this.profession = profession;
   }

   @XmlElement
   public String getVorname()
   {
      return vorname;
   }

   public void setVorname(String vorname)
   {
      this.vorname = vorname;
   }

   @XmlElement
   public String getNachname()
   {
      return nachname;
   }

   public void setNachname(String nachname)
   {
      this.nachname = nachname;
   }

   @XmlAttribute
   public String getProfession()
   {
      return profession;
   }

   public void setProfession(String profession)
   {
      this.profession = profession;
   }

   @Override
   public String toString()
   {
      return vorname + " " + nachname + "\n" + profession;
   }
}

Erzeugt man ein Objekt dieser Klasse mit z. Bsp. dem Vornamen "Guy de" und dem Nachnamen "Maupassant", so kann der javax.xml.bind.Marshaller daraus die folgende Xml-Datei generieren.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person profession="Writer">
    <nachname>Maupassant</nachname>
    <vorname>Guy de</vorname>
</person>

Mit diesem Beispiel kann man sofort die folgenden kleinen Regeln bestätigen.


Erste Regeln

Es ist auch möglich die Annotations direkt im Datenteil zu setzen, dann dürfen sie allerdings nicht mehr bei den setters und getters stehen. Überdies muß das durch eine eigene Annotations vor dem Anfang der Klasse angekündigt werden.

Hier diese Variation des gleichen Beispiels. Sie führt zur selben Xml-Datei.

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;  // wird nicht von Eclipse erkannt

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class Person
{
   @XmlElement
   private String vorname;
   private String nachname;
   @XmlAttribute
   private String profession;

   /**   Defaultkonstruktor notwendig
   */
   public Person()
   {}

   /**
    */
   public Person(String vorname, String nachname, String profession)
   {
      this.vorname = vorname;
      this.nachname = nachname;
      this.profession = profession;
   }

   public String getVorname()
   {
      return vorname;
   }

   public void setVorname(String vorname)
   {
      this.vorname = vorname;
   }

   public String getNachname()
   {
      return nachname;
   }

   public void setNachname(String nachname)
   {
      this.nachname = nachname;
   }

   public String getProfession()
   {
      return profession;
   }

   public void setProfession(String profession)
   {
      this.profession = profession;
   }

   @Override
   public String toString()
   {
      return vorname + " " + nachname + "\n" + profession;
   }
}

Dies führt uns zu:


Weitere kleine Regeln

Marshalling: Ein Java Objekt auf eine Xml-Datei abbilden.

Dank JAXB ist das mit wenigen Zeilen erledigt.

import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

//...

try
{
   Person person = new Person("Guy de", "Maupassant", "Writer");
   File xmlfile = new File("person.xml");
   JAXBContext jaxbContext = JAXBContext.newInstance(Person.class);
   Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

   // formatiert die Datei, ergänzt Zeilenumbrüche zur besseren Lesbarkeit
   jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

   // Speichert das Objekt person in einer als Xml-Datei person.xml
   jaxbMarshaller.marshal(person, xmlfile);
}
catch(JAXBException ex)
{
   System.out.println(ex);
}

Unmarshalling: Mit einer Xml-Datei ein Java Objekt erzeugen

Voraussetzung ist hier, daß die Javaklasse (Person.java) existiert.

import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

...

try
{
   File xmlfile = new File("person.xml");
   JAXBContext jaxbContext = JAXBContext.newInstance(Person.class);
   Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
   Person person = (Person) jaxbUnmarshaller.unmarshal(xmlfile);
}
catch(JAXBException ex)
{
   System.out.println(ex);
}

Eine Javaklasse aus einer DTD erzeugen

Mit Hilfe des Konsoltools xjc in %JDK-Home%/bin kann man sich aus einer DTD (Document Type Definition) eine Javaklasse erzeugen lassen. Wir zeigen das an Hand der folgenden DTD.

<?xml version="1.0" encoding="US-ASCII" ?>
<!ELEMENT adressen (adresse)>
<!ELEMENT adresse (name, strasse, stadt)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT strasse (#PCDATA)>
<!ELEMENT stadt (#PCDATA)>

Der Aufruf ist sehr einfach

xjc -dtd adressen.dtd

xjc legt in einem Unterverzeichnis namens generated die folgenden Dateien an:

Hier der um die Kommentare gekürzte Quellcode:

Klasse Adresse

// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
// Any modifications to this file will be lost upon recompilation of the source schema.

package generated;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "name",
    "strasse",
    "stadt"
})
@XmlRootElement(name = "adresse")
public class Adresse {

    @XmlElement(required = true)
    protected String name;
    @XmlElement(required = true)
    protected String strasse;
    @XmlElement(required = true)
    protected String stadt;

    public String getName() {
        return name;
    }

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

    public String getStrasse() {
        return strasse;
    }

    public void setStrasse(String value) {
        this.strasse = value;
    }

    public String getStadt() {
        return stadt;
    }

    public void setStadt(String value) {
        this.stadt = value;
    }

}

Es fällt auf, daß der Datenteil protected und nicht private ist.

Klasse Adressen

package generated;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "adresse"
})
@XmlRootElement(name = "adressen")
public class Adressen {

    @XmlElement(required = true)
    protected Adresse adresse;

    public Adresse getAdresse() {
        return adresse;
    }

    public void setAdresse(Adresse value) {
        this.adresse = value;
    }
}

Auch hier ist der Datenteil protected und nicht private.

Klasse ObjektFactory

package generated;

import javax.xml.bind.annotation.XmlRegistry;

/**
 * This object contains factory methods for each Java content interface and Java element interface
 * generated in the generated package.

 * An ObjectFactory allows you to programatically construct new instances of the Java representation
 * for XML content. The Java representation of XML content can consist of schema derived interfaces
 * and classes representing the binding of schema type definitions, element declarations and model
 * groups.  Factory methods for each of these are provided in this class.
 */
@XmlRegistry
public class ObjectFactory {
    /**
     * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: generated
     */
    public ObjectFactory() {
    }

    /**
     * Create an instance of {@link Adressen }
     */
    public Adressen createAdressen() {
        return new Adressen();
    }

    /**
     * Create an instance of {@link Adresse }
     */
    public Adresse createAdresse() {
        return new Adresse();
    }
}

Den Datenteil private erzeugen

Leider gibt es keinen Parameter um xjc dazu zu bewegen, den Datenteil private zu erzeugen. Eine Lösung hierzu findet man jedoch auf

stackoverflow: why-do-jaxb-generated-classes-have-protected-members-and-how-can-i-change-this


Eine Javaklasse auf eine xsd-Datei abbilden

Das Beispiel ist die Vervollständigung einer Idee von Blaise Doughan, siehe stackoverflow: is-it-possible-to-generate-a-xsd-from-a-jaxb-annotated-class

Mit Hilfe der Methode generateSchema() aus der Klasse JAXBContext ist es möglich aus eine Javaklasse eine xsd-Datei zu erzeugen. Dieser Methode muß ein Objekt der Klasse SchemaOutputResolver übergeben werden. Dazu schreibt man eine Unterklasse der abstrakten Klasse SchemaOutputResolver und überschreibt die Methode createOutput(). In dieser Methode ignoriert man den übergebenen Dateinamen, der immer "schema1.xsd" ist, und setzt einen eigenen.

Hier die Codesnippets.

try
{
   JAXBContext jaxbContext = JAXBContext.newInstance(Adresse.class);
   SchemaOutputResolver sor = new MySchemaOutputResolver("Adresse.xsd");
   jaxbContext.generateSchema(sor);
}
catch(JAXBException | IOException ex)
{
   ex.printStackTrace();
}

Die Klasse MySchemaOutputResolver.

class MySchemaOutputResolver extends SchemaOutputResolver
{
   private String filename;

   public MySchemaOutputResolver(String filename)
   {
      this.filename = filename;
   }

   @Override
   public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException
   {
      // ignore suggestedFileName
      //System.out.println("suggestedFileName = " + suggestedFileName); // schema1.xsd
      File file = new File(filename);
      StreamResult result = new StreamResult(file);
      result.setSystemId(file.toURI().toURL().toString());
      return result;
   }

}

Eine Javaklasse aus einer xsd-Datei erzeugen

Hier kann man wieder das Konsoltool xjc einsetzen. Ein Aufrufbeispiel:

xjc adressen.xsd










Valid XHTML 1.0 Strict top Back Next Up Home