Using JAXB

JAXB allows for a mapping between Java objects and XML files. The following example goes through a couple steps so you get a feel of this powerful library.

Download the early-access implementation from http://java.sun.com/xml/jaxb, and add the JAR files to your classpath.

First, write a DTD that describes your object.
customer.dtd (!!remove the space between the ? and xml) :

<? xml version="1.0" encoding="UTF-8"?>
<!ELEMENT customer (name, addresses+, email)+ >
<!ATTLIST customer custid CDATA #REQUIRED>
 
<!ELEMENT name (#PCDATA)>
 
<!ELEMENT addresses (addline1, addline2, zip, location, state)>
<!ELEMENT addline1 (#PCDATA)>
<!ELEMENT addline2 (#PCDATA)>
<!ELEMENT zip (#PCDATA)>
<!ELEMENT location (#PCDATA)>
<!ELEMENT state (#PCDATA)>
 
<!ELEMENT email (#PCDATA)>

To map this description of a customer to an object (tree), you can invoke the schema to java compiler:

   java com.sun.tools.xjc.Main customer.dtd -roots customer

output:

   .Addresses.java
   .Customer.java
   .Email.java
   .Name.java

Two files have been created that are the equivalent of the DTD. You can further customize the mapping by creating a .xjs file. For example, in the following customer.xjs file, it is specified that the attribute custid is mapped onto an int in the Java object (the default was a String). Also, the classes are to be placed in the package
com.esus.jaxbtest.
customer.xjs (!!remove the space between ? and xml):

<? xml version="1.0" encoding="UTF-8"?>
<xml-java-binding-schema version="1.0ea">
  <options package="com.esus.jaxbtest"/>
 
  <element name="customer" type="class" root="true">
    <attribute name="custid" convert="long"/>
  </element>
</xml-java-binding-schema>

Now run the conversion utility again, but specify your customized mapping:

   java com.sun.tools.xjc.Main customer.dtd customer.xjs

output is now:

   .comesusjaxbtestAddresses.java
   .comesusjaxbtestCustomer.java
   .comesusjaxbtestEmail.java
   .comesusjaxbtestName.java

Now, let’s test the mapping and create an XML file data.xml that describes one customer.

data.xml (!!remove the space between ? and xml):

<? xml version="1.0"?>
<customer>
   <name>Jefke McCann</name>
   <addresses>
      <addline1>516, S. 5th Ave.</addline1>
      <zip>59715</zip>
      <location>Bozeman</location>
      <state>MT</state>
   </addresses>
   <email>jefke.mccann@yahoo.com</email>
</customer>

java Main:

C:myxmljaxb>java Main
javax.xml.bind.MissingContentException: addline2
        at Addresses.validateThis(Addresses.java:93)
        at javax.xml.bind.Unmarshaller.unmarshal(Unmarshaller.java:209)
        at javax.xml.bind.Unmarshaller.unmarshal(Unmarshaller.java:133)
        at Customer.unmarshal(Customer.java:161)
        at javax.xml.bind.Unmarshaller.unmarshal(Unmarshaller.java:199)
        at javax.xml.bind.Unmarshaller.unmarshalRoot(Unmarshaller.java:222)
        at javax.xml.bind.Dispatcher.unmarshal(Dispatcher.java:350)
        at Customer.unmarshal(Customer.java:198)
        at Customer.unmarshal(Customer.java:192)
        at Customer.unmarshal(Customer.java:186)
        at Main.main(Main.java:10)

Notice that the conversion halts because data.xml does not contain the tag addline2 while it was required in the original DTD customer.dtd. Let’s make addline2 optional, and regenerate.

customer.dtd (!!remove the space between ? and xml):

<? xml version="1.0" encoding="UTF-8"?>
<!ELEMENT customer (name, addresses+, email)+ >
<!ATTLIST customer custid CDATA #REQUIRED>
 
<!ELEMENT name (#PCDATA)>
 
<!ELEMENT addresses (addline1, addline2?, zip, location, state)>
<!ELEMENT addline1 (#PCDATA)>
<!ELEMENT addline2 (#PCDATA)>
<!ELEMENT zip (#PCDATA)>
<!ELEMENT location (#PCDATA)>
<!ELEMENT state (#PCDATA)>
 
<!ELEMENT email (#PCDATA)>
C:myxmljaxb>del *.class
 
C:myxmljaxb>java com.sun.tools.xjc.Main customer.dtd customer.xjs
.comesusjaxbtestAddresses.java
.comesusjaxbtestCustomer.java
.comesusjaxbtestEmail.java
.comesusjaxbtestName.java
 
C:myxmljaxb>javac Main.java
 
C:myxmljaxb>java Main
javax.xml.bind.MissingAttributeException: custid
        at com.esus.jaxbtest.Customer.validateThis(Customer.java:82)
        at javax.xml.bind.Unmarshaller.unmarshal(Unmarshaller.java:209)
        at javax.xml.bind.Unmarshaller.unmarshalRoot(Unmarshaller.java:222)
        at javax.xml.bind.Dispatcher.unmarshal(Dispatcher.java:350)
        at com.esus.jaxbtest.Customer.unmarshal(Customer.java:156)
        at com.esus.jaxbtest.Customer.unmarshal(Customer.java:150)
        at com.esus.jaxbtest.Customer.unmarshal(Customer.java:144)
        at Main.main(Main.java:10)

Notice now that the unmarshalling fails because our customer record does not contain the required custid attribute. Add it!
data.xml (!!remove the space between ? and xml):

<? xml version="1.0"?>
<customer custid="12345">
   <name>Jefke McCann</name>
   <addresses>
      <addline1>516, S. 5th Ave.</addline1>
      <zip>59715</zip>
      <location>Bozeman</location>
      <state>MT</state>
   </addresses>
   <email>jefke.mccann@yahoo.com</email>
</customer>

Now run again:

C:myxmljaxb>java Main
<<customer custid=12345 content=[<<name content=Jefke McCann>>, <<addresses addl
ine1=516, S. 5th Ave. zip=59715 location=Bozeman state=MT>>, <<email content=jef
ke.mccann@yahoo.com>>]>>

To go the other way:
Main.java:

import com.esus.jaxbtest.*;
import java.util.*;
import java.io.*;
 
public class Main
{
   public static Customer cust;
 
   public static void main(String []args) {
      try {
         cust = new Customer();
 
         buildTree();
         validate();
         marshal();
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   }
 
   public static void buildTree() throws Exception {
      // set custid attribute
      cust.setCustid(12345);
      List custEntries = cust.getContent();
      Name name = new Name();
      name.setContent("Joris Van den Bogaert");
      custEntries.add(name);
      Email email = new Email();
      email.setContent("joris1@esus.com");
      custEntries.add(email);
      Addresses address1 = new Addresses();
      address1.setAddline1("A. Dewitstraat 50");
      address1.setZip("3078");
      address1.setLocation("Meerbeek");
      address1.setState("BE");
      Addresses address2 = new Addresses();
      address2.setAddline1("Handelskaai 3");
      address2.setZip("1000");
      address2.setLocation("Brussel");
      address2.setState("BE");
      custEntries.add(address1);
      custEntries.add(address2);
   }
 
   public static void validate() throws Exception {
      cust.validate();
   }
 
   public static void marshal() throws Exception {
      FileOutputStream out = new FileOutputStream("out.xml");
      try {
         cust.marshal(out);
      } finally {
         out.close();
      }	
   }
}

After executing this program, the following file out.xml will be created:

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

<customer custid="12345">
  <name>Joris Van den Bogaert</name>
  <email>joris1@esus.com</email>
  <addresses>
    <addline1>A. Dewitstraat 50</addline1>
    <zip>3078</zip>
    <location>Meerbeek</location>
    <state>BE</state></addresses>
  <addresses>
    <addline1>Handelskaai 3</addline1>
    <zip>1000</zip>
    <location>Brussel</location>
    <state>BE</state></addresses>
</customer>