Exchanging Data with XML and JAXB, Part 1

by Jennie Hall

In this tip, you'll learn how to to use the Java Architecture for XML Binding (JAXB) in Java SE 6 to exchange XML data between systems without having to delve into the specifics of XML processing. Among other key improvements, JAXB 2.0 offers support for binding Java objects to XML via the use of annotations. This feature allows you to generate XML data from your application simply by annotating your existing object model.

What's JAXB?

JAXB simplifies the use of XML data in Java applications by shielding you and your code from the low-level details of working with XML. Essentially, JAXB allows you to move easily back-and-forth between XML and Java. A JAXB implementation supplies a schema compiler, which takes in an XML schema and generates Java classes which map to that schema. Data from XML documents which are instances of the schema can be bound automatically to the generated classes by JAXB's binding runtime framework. This is called unmarshalling. Once unmarshalled, content can be manipulated or modified in Java as needed. JAXB can also write (marshal) data from Java objects to an XML instance document. JAXB optionally performs validation of content as part of these operations.

In addition to the schema compiler, a JAXB implementation also provides a schema generator, which looks at annotations on Java classes and generates a corresponding XML schema. This feature is new to JAXB 2.0.

The Sample Application

A veterinary office wants to send notices to its clients reminding them of their pets' upcoming appointments. Because they are nice folks, they also like to send a birthday card to an owner on the pet's birthday. The veterinary office, NiceVet, contracts with a service provider, WePrintStuff, to do their printing and mailing for them.

NiceVet already has an application which maintains information about the animals under NiceVet's care and their owners. Here's a diagram of NiceVet's existing object model:


NiceVet needs a way to get the necessary notification data to WePrintStuff for processing without spending a lot of time and money modifying their current application.

Generating the XML

It's time to annotate our object model so we can generate some XML data for the printing and mailing service. Looking at NiceVet's object model, we see that the PrintOrder class holds the information about upcoming events, and thus makes sense as the root element of the data we want to send. Let's add the @XmlRootElement annotation to the PrintOrder class. This will establish printOrder as the root element of our generated XML.

	import javax.xml.bind.annotation.XmlRootElement;

	// the root element of our generated XML
	@XmlRootElement(name = "printOrder")
	public class PrintOrder {

	    private long id;
	    private List<Event> events;

Let's add some more annotations:

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

	// the root element of our generated XML
	@XmlRootElement(name = "printOrder")
	// maps this class to a generated schema type
	@XmlType(name = "PrintOrderType", propOrder = {"events"})
	// bind all non-static, non-transient fields
	// to XML unless annotated with @XmlTransient
	@XmlAccessorType(XmlAccessType.FIELD)
	public class PrintOrder {

	    // maps this field to an XML attribute
	    @XmlAttribute(required = true)
	    private long id;
	    // custom adapter maps this field to a type,

	    // NotificationsType, which makes it easy to
	    // generate the desired XML
	    @XmlJavaTypeAdapter(value=EventAdapter.class)   
	    @XmlElement(name = "notifications")
	    private List<Event> events;

@XmlType maps the PrintOrder class to a generated schema type named PrintOrderType. The @XmlAccessorType(XmlAccessType.FIELD) annotation tells JAXB to bind all non-static, non-transient fields to XML unless annotated with @XmlTransient, which prevents the mapping of a field or property. With the XmlAccessType.FIELD setting, getter/setter pairs are not bound to XML unless explicitly annotated. The default setting is XmlAccessType.PUBLIC_MEMBER (all public fields and getter/setter pairs are bound unless otherwise annotated); other settings are XmlAccessType.PROPERTY and XmlAccessType.NONE.

Moving down the code listing, we see that the field id maps to an XML attribute on the PrintOrderType element. Some kind of identifier on the print order is required, and we've annotated the id field accordingly.

The PrintOrder class has a list of events which contains both appointments and birthdays. The annotation @XmlJavaTypeAdapter tells JAXB that we are going to use a custom adapter, EventAdapter, to help us map this Java construct to XML. To support this annotation, we need to write an adapter class which extends XmlAdapter and overrides its abstract marshal and unmarshal methods.

	public class EventAdapter extends XmlAdapter<NotificationsType, List<Event>> {

	    // adapt original Java construct to a type, NotificationsType,
	    // which we can easily map to the XML output we want
	    public NotificationsType marshal(List<Event> events) throws Exception {
	        List<Appointment> appointments = new ArrayList<Appointment>();
	        List<Birthday> birthdays = new ArrayList<Birthday>();
	        
	        for (Event e : events) {
	            if (e instanceof Appointment) {
	                appointments.add((Appointment)e);
	            } else {
	                birthdays.add((Birthday)e);              
	            }
	        }        
	        return new NotificationsType(appointments, birthdays);
	    }

	    // reverse operation: map XML type to Java
	    public List<Event> unmarshal(NotificationsType notifications) throws Exception { ... }
        

EventAdapter's marshal method takes the original Java construct, List<Event> events, and converts it to a type which maps more easily to the XML output we want. Let's take a look at NotificationsType:

	import javax.xml.bind.annotation.XmlElementWrapper;
	import javax.xml.bind.annotation.XmlElement;
	import java.util.List;

	public class NotificationsType {
	    
	    // produce a wrapper XML element around this collection
	    @XmlElementWrapper(name = "appointments")
	    // maps each member of this list to an XML element named appointment
	    @XmlElement(name = "appointment")
	    //private List<Appointment> appointments;
	    private List<Appointment> appointments;
	    // produce a wrapper XML element around this collection
	    @XmlElementWrapper(name = "birthdays")
	    // maps each member of this list to an XML element named birthday
	    @XmlElement(name = "birthday")
	    //private List<Birthday> birthdays;
	    private List<Birthday> birthdays;
	    
	    public NotificationsType() {}
	    
	    public NotificationsType(List<Appointment> appointments, List<Birthday> birthdays) {
	        this.appointments = appointments;
	        this.birthdays = birthdays;
	    }		

The @XmlElementWrapper annotation creates a wrapper XML element around each collection, and the @XmlElement annotation maps each member of the collection to an XML element of the appropriate type. Combined with the @XmlElement(name = "notifications") annotation on PrintOrder.events, what we end up with will look something like this:

	<printOrder>
		<notifications>
			<appointments>
				<appointment>
					...
				</appointment>
			</appointments>
			<birthdays>
				<birthday>
					...
				</birthday>
			</birthdays>
		</notifications>
	</printOrder>

Another annotation worth mentioning lies in the abstract base class Event (listing below), from which subclasses Appointment and Birthday inherit. This inheritance hierarchy won't hold much meaning for our service provider, WePrintStuff, so we'll eliminate it with the @XmlTransient annotation on Event. We still need fields owner and pet, however, so we annotate them with @XmlElement(required = true).

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

	// no need to capture this inheritance hierarchy in XML
	@XmlTransient
	public abstract class Event {

	    @XmlElement(required = true)
	    private Owner owner;
	    @XmlElement(required = true)
	    private Pet pet;

	    public Event() {}
	            
	    public Event(Owner owner, Pet pet) {
	       this.owner = owner;
	       this.pet = pet;
	    }

When objects of type Birthday and Appointment are marshalled to XML, they will include all the relevant data from their supertype, Event. Take a look at the listing below for the Birthday class:

	@XmlType(name = "BirthdayType", propOrder = {"age", "birthday", "owner", "pet"})
	@XmlAccessorType(XmlAccessType.FIELD)
	public class Birthday extends Event {

	    @XmlElement(required = true)
	    private int age;
	    
	    public Birthday() {}
	    
	    public Birthday(Owner owner, Pet pet) {
	        super(owner, pet);
	        this.age = this.calculateAge();
	    }

	    @XmlElement(name = "birthday", required = true)
	    public Date getBirthday() {
	        return this.getPet().getDateOfBirth();
	    }

@XmlType.propOrder specifies the order in which content is marshalled and unmarshalled. This order would otherwise be unspecified due to the nature of Java reflection. Alternatively, you can impose an order with the @XmlAccessorOrder annotation. As mentioned in the Java Tutorial, imposing an order improves application portability across JAXB implementations.

Although the field dateOfBirth is actually on the Pet class, it makes more sense in terms of our XML representation if this data is organized as an element under the birthday element. We can achieve this without a dateOfBirth field on the Birthday class by annotating the getBirthday() method, which delegates to Pet.

On a related note, WePrintStuff doesn't need to do any additional processing with the dates NiceVet sends them; they just need to locate the correct notification template and print the information out. As we saw earlier with the events list on the PrintOrder class, we can supply a custom adapter to get the desired XML output. This time, however, we'll specify the adapter through a package-level annotation. The adapter will take in all java.util.Date instances and output nicely formatted Strings. The package-level annotation goes in the package-info.java file (located in the vet package):

	// every java.util.Date class in the vet package should be
	// processed by DateAdapter
	@XmlJavaTypeAdapter(value=DateAdapter.class, type=Date.class)
	package vet;

	import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
	import java.util.Date;
    

Although JAXB can handle marshalling java.util.Date to XML in a default manner on its own, we're telling JAXB to use an alternative, customized mapping which better suits our purposes. We implement the adapter in much the same way as above:

	import java.util.Date;
	import java.text.SimpleDateFormat;
	import javax.xml.bind.annotation.adapters.XmlAdapter;

	public class DateAdapter extends XmlAdapter<String, Date> {

		// the desired format
	    private String pattern = "MM/dd/yyyy";
	    
	    public String marshal(Date date) throws Exception {
	        return new SimpleDateFormat(pattern).format(date);
	    }
	    
	    public Date unmarshal(String dateString) throws Exception {
	        return new SimpleDateFormat(pattern).parse(dateString);
	    }	
	
  

The annotations on the other classes in the object model are similar to what we've seen so far; take a peek at the sample code included with this tip to see exactly what's going on in the other classes.

We've finished annotating NiceVet's object model and we're ready to generate some XML. If we run the sample application, we should see an XML instance document something like the following listing. The document, niceVet.xml, is located in the root directory of the java-to-schema project.

	<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
	<printOrder id="1218226109781">
	    <notifications>
	        <appointments>
	            <appointment>
	                <apptType>Yearly Checkup</apptType>
	                <apptDate>09/15/2008</apptDate>
	                <owner>
	                    <firstName>Joe</firstName>
	                    <lastName>Outdoors</lastName>
	                    <address>
	                        <addressLine1>123 Whitewater Street</addressLine1>
	                        <city>OurTown</city>
	                        <state>CA</state>
	                        <zip>90347</zip>
	                        <zipExt>1234</zipExt>
	                    </address>
	                </owner>
	                <pet>
	                    <name>Honcho</name>
	                    <species>Dog</species>
	                </pet>
	            </appointment>
	            <appointment>
	                <apptType>Well Mom Exam</apptType>
	                <apptDate>09/12/2008</apptDate>
	                <owner>
	                    <firstName>Missy</firstName>
	                    <lastName>Fairchild</lastName>
	                    <address>
	                        <addressLine1>456 Scenic Drive</addressLine1>
	                        <city>West OurTown</city>
	                        <state>CA</state>
	                        <zip>90349</zip>
	                        <zipExt>6789</zipExt>
	                    </address>
	                </owner>
	                <pet>
	                    <name>Miss Kitty</name>
	                    <species>Cat</species>
	                </pet>
	            </appointment>
	        </appointments>
	        <birthdays>
	            <birthday>
	                <age>7</age>
	                <birthday>09/07/2000</birthday>
	                <owner>
	                    <firstName>Violet</firstName>
	                    <lastName>Flowers</lastName>
	                    <address>
	                        <addressLine1>22375 Willow Court</addressLine1>
	                        <city>West OurTown</city>
	                        <state>CA</state>
	                        <zip>90349</zip>
	                        <zipExt>6789</zipExt>
	                    </address>
	                </owner>
	                <pet>
	                    <name>Tom</name>
	                    <species>Cat</species>
	                </pet>
	            </birthday>
	        </birthdays>
	    </notifications>
	</printOrder>

The generated schema for the XML instance document above is named schema1.xsd, and it lives in /generated/schema under the root directory of the java-to-schema project. It looks like this:

	<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
	<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

	  <xs:element name="printOrder" type="PrintOrderType"/>

	  <xs:complexType name="PrintOrderType">
	    <xs:sequence>
	      <xs:element name="notifications" type="notificationsType" minOccurs="0"/>
	    </xs:sequence>
	    <xs:attribute name="id" type="xs:long" use="required"/>
	  </xs:complexType>

	  <xs:complexType name="notificationsType">
	    <xs:sequence>
	      <xs:element name="appointments" minOccurs="0">
	        <xs:complexType>
	          <xs:sequence>
	            <xs:element name="appointment" type="AppointmentType" minOccurs="0" maxOccurs="unbounded"/>
	          </xs:sequence>
	        </xs:complexType>
	      </xs:element>
	      <xs:element name="birthdays" minOccurs="0">
	        <xs:complexType>
	          <xs:sequence>
	            <xs:element name="birthday" type="BirthdayType" minOccurs="0" maxOccurs="unbounded"/>
	          </xs:sequence>
	        </xs:complexType>
	      </xs:element>
	    </xs:sequence>
	  </xs:complexType>

	  <xs:complexType name="AppointmentType">
	    <xs:sequence>
	      <xs:element name="apptType" type="xs:string"/>
	      <xs:element name="apptDate" type="xs:string"/>
	      <xs:element name="owner" type="OwnerType"/>
	      <xs:element name="pet" type="PetType"/>
	    </xs:sequence>
	  </xs:complexType>

	  <xs:complexType name="OwnerType">
	    <xs:sequence>
	      <xs:element name="firstName" type="xs:string"/>
	      <xs:element name="lastName" type="xs:string"/>
	      <xs:element name="address" type="AddressType"/>
	    </xs:sequence>
	  </xs:complexType>

	  <xs:complexType name="AddressType">
	    <xs:sequence>
	      <xs:element name="addressLine1" type="xs:string"/>
	      <xs:element name="addressLine2" type="xs:string" minOccurs="0"/>
	      <xs:element name="city" type="xs:string"/>
	      <xs:element name="state" type="xs:string"/>
	      <xs:element name="zip" type="xs:string"/>
	      <xs:element name="zipExt" type="xs:string" minOccurs="0"/>
	    </xs:sequence>
	  </xs:complexType>

	  <xs:complexType name="PetType">
	    <xs:sequence>
	      <xs:element name="name" type="xs:string"/>
	      <xs:element name="species" type="xs:string" minOccurs="0"/>
	    </xs:sequence>
	  </xs:complexType>

	  <xs:complexType name="BirthdayType">
	    <xs:sequence>
	      <xs:element name="age" type="xs:int"/>
	      <xs:element name="birthday" type="xs:string"/>
	      <xs:element name="owner" type="OwnerType"/>
	      <xs:element name="pet" type="PetType"/>
	    </xs:sequence>
	  </xs:complexType>
	</xs:schema>

  

If we take a quick peek at the main() method, we can see that we are generating the schema as follows:

	// specify where the generated XML schema will be created
	final File dir = new File("generated" + File.separator + "schema");
	// specify a name for the generated XML instance document
	OutputStream os = new FileOutputStream("niceVet.xml");

	// create a JAXBContext for the PrintOrder class
	JAXBContext ctx = JAXBContext.newInstance(PrintOrder.class);
	// generate an XML schema from the annotated object model; create it
	// in the dir specified earlier under the default name, schema1.xsd
	ctx.generateSchema(new SchemaOutputResolver() {
		   @Override
		   public Result createOutput(String namespaceUri, String schemaName) throws IOException {
				return new StreamResult(new File(dir, schemaName));
		   }
	   });

To get the XML instance document, we first create some data, then we obtain a Marshaller from the JAXBContext and marshal the Java content tree to XML:

	// create some data
	PrintOrder order = new PrintOrder(EventUtil.getEvents());

		...
		
	// create a marshaller   
	Marshaller marshaller = ctx.createMarshaller();
	// the property JAXB_FORMATTED_OUTPUT specifies whether or not the
	// marshalled XML data is formatted with linefeeds and indentation
	marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
	// marshal the data in the Java content tree 
	// to the XML instance document niceVet.xml
	marshaller.marshal(order, os);		

Running the Sample Application


To run the sample application, download the sample code and unzip it. Launch NetBeans and select File -> Open Project. In the Open Project dialog box, navigate to the directory where you unzipped the sample code and select the folder java-to-schema. Select the Open as Main Project check box. Click Open Project Folder. Right-click the java-to-schema project and select Run Project.

Conclusion

In this tip, you've seen how JAXB makes it easy to generate XML data to exchange with a business partner. The XML data your partner receives, however, may need a little tweaking in order to mesh with their system. In a future tip, you'll learn how to apply customizations to a supplied schema to get the Java object model - and content tree - you want.

References and Resources

  • Sample code for this tip
  • The Java Tutorial.
  • Kohsuke Kawaguchi's Blog. Kohsuke Kawaguchi is a staff engineer at Sun Microsystems, where among other projects he works on JAXB.
  • Jennie Hall is a lead developer working in the financial sector.

    Comments:

    Excellent!

    Posted by john richards on August 15, 2008 at 04:21 AM PDT #

    The sample code is not available. Got a 404! It's a pity!

    Regards, Stefan

    Posted by stefan222 on August 19, 2008 at 03:46 PM PDT #

    these tips relly do help

    Posted by guest on August 20, 2008 at 03:13 AM PDT #

    I have fixed the download link and apologize for the error!

    Dana Nourie
    Sun Microsystems

    Posted by Dana Nourie on August 20, 2008 at 03:18 AM PDT #

    すばらしいかったです

    Posted by guest on August 20, 2008 at 11:18 AM PDT #

    excellent

    Posted by vikram on August 20, 2008 at 04:48 PM PDT #

    I found this really helpful. Thanks

    Posted by Lois Miller on August 21, 2008 at 03:22 AM PDT #

    Though I haven't searched recently, articles related to JAXB custom mapping are really scarce, I think. I would expect more advanced tutorial for corner cases also.

    Thanks for the good job.

    Posted by Hafizur Rahman on August 21, 2008 at 09:53 PM PDT #

    is good that u expand ur experience to other

    Posted by femi on August 21, 2008 at 10:07 PM PDT #

    I am using JAXB to generate XML files and also to transform one XML into other XML via XSLT. My code works fine in stand-alone mode. But, it fails to create complete XML files when I run it as JMX scheduler service. All I get is a blank or sometimes single line (<?xml ?> ) file.
    It would be really helpful, if you could throw some light.

    Posted by Prafull Kotecha on August 22, 2008 at 06:45 AM PDT #

    good

    Posted by guest on August 24, 2008 at 06:53 PM PDT #

    Thanks for simplified but comprehensive example.

    Posted by Erkin on August 25, 2008 at 07:51 PM PDT #

    very good work, be dont understand ur mail that u are send to me.

    Posted by femi on August 26, 2008 at 08:04 PM PDT #

    WOW! That was a really helpful article!!! Thank you so much!

    That schema generation feature... Great!

    After reading this, I've played only few minutes with JAXB, but I can already say: it's a great API.

    Posted by Ivan on September 25, 2008 at 06:51 AM PDT #

    If want to use xs:choice in xsd.
    how to generate by annotation .

    please kindly tell me.

    Thank a lot
    Pluck

    Posted by Super Pluck on September 28, 2008 at 07:02 PM PDT #

    can you use jaxb2.1 in javase5?

    Posted by allen on October 01, 2008 at 09:36 PM PDT #

    The best article I have found.
    Thank you, it is very usefull.

    Posted by tatsyana on October 08, 2008 at 09:20 PM PDT #

    The best article I have found.
    Thank you, it is very usefull.

    Posted by tatsyana on October 08, 2008 at 09:41 PM PDT #

    Post a Comment:
    Comments are closed for this entry.
    About

    John O'Conner

    Search

    Categories
    Archives
    « April 2014
    SunMonTueWedThuFriSat
      
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
       
           
    Today