Java-XML mapping made easy with JAXB 2.0

The new Java Architecture for XML Binding makes it easy to incorporate XML data and processing into Java applications

The Java Architecture for XML Binding provides a powerful and practical way of working with XML content from within Java applications. The newly released JAXB 2.0 offers many new features, including full support of all XML Schema features, significantly fewer generated classes, generated classes that are easier to manipulate, and a more flexible validation mechanism.

To understand how to process XML documents in Java with JAXB 2.0, we need to look at the two main JAXB components:

  • The binding compiler, which binds a given XML schema to a set of generated Java classes
  • The binding runtime framework, which provides unmarshalling, marshalling, and validation functionalities

The JAXB binding compiler (or xbj) lets you generate Java classes from a given XML schema. The JAXB binding compiler transforms an XML schema into a collection of Java classes that match the structure described in the XML schema. These classes are annotated with special JAXB annotations, which provide the runtime framework with the mappings it needs to process the corresponding XML documents.

The binding runtime framework provides an efficient and easy-to-use mechanism for unmarshalling (or reading) and marshalling (or writing) XML documents. It lets you transform an XML document into a hierarchy of Java objects (unmarshalling) or, inversely, transform a Java object hierarchy into XML format (marshalling). The term marshalling traditionally refers to disposing troops in some suitable manner. In networking, it refers to placing data items into a buffer before sending them over a communication channel.

Combined, these two components produce a technology that lets Java developers easily manipulate XML data in the form of Java objects, without having to know the nitty-gritty details of the Simple API for XML Processing (SAX) or the Document Object Model (DOM), or even the subtleties of XML Schema.

JAXB prerequisites

To get started with JAXB 2.0 you need:

  • Java Platform, Standard Edition 5: JAXB 2.0 relies heavily on features of Java SE 5, such as annotations and generics
  • An implementation of JAXB 2.0

This article was written using the GlassFish JAXB reference implementation release candidate.

Generate Java classes using the JAXB compiler

The JAXB compiler binds an XML schema to a set of Java classes. An XML schema is an XML document that describes, very precisely, the elements and attributes authorized in a certain type of XML document. In this example, we use a training course booking system that can accept orders in XML format. A typical order looks like this:

   <?xml version="1.0" encoding="UTF-8"?>
    <booking xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <company name="ACME Consulting">
            <address>10 Coyote Avenue,  Arizona, USA</address>
            <contact name="Duke" email="duke@acme.com" telephone="1234567890"/>
        </company>
        <student firstName="Jane" surname="Dow"/>
        <student firstName="John" surname="Doe"/>
    </booking>  

The corresponding XML schema describes how the training course is booked, and contains details of the booked course, the enrolled students, the company making the booking, and so forth. An XML schema description is extremely rigorous and can include details such as the number of elements allowed in a list of objects (cardinality), optional and mandatory attributes, and more. The schema for the training course bookings (called course-booking.xsd) is shown here:

   <?xml version="1.0" encoding="UTF-8"?>
    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     <xsd:element name="booking" type="courseBooking"/>
     <xsd:complexType name="courseBooking">
      <xsd:sequence>
       <xsd:element ref="company" />
       <xsd:element ref="student"  minOccurs="1" maxOccurs="unbounded"/>
      </xsd:sequence>
      <xsd:attribute name="courseReference" type="xsd:string"  use="required"/>
      <xsd:attribute name="courseDate" type="xsd:date"  use="required"/>
      <xsd:attribute name="invoiceReference" type="xsd:string"  use="required"/>
      <xsd:attribute name="totalPrice" type="xsd:decimal"  use="required"/>
     </xsd:complexType>
     <xsd:element name="student" type="studentType"/>
     <xsd:complexType name="studentType">
       <xsd:attribute name="firstName"  type="xsd:string" use="required"/>
       <xsd:attribute name="surname" type="xsd:string" use="required"/>
     </xsd:complexType>
     <xsd:element name="company" type="companyType"/>
     <xsd:complexType name="companyType">
      <xsd:sequence>
       <xsd:element name="address"/>
       <xsd:element ref="contact" />
      </xsd:sequence>
      <xsd:attribute name="name" type="xsd:string"/>
     </xsd:complexType>
      <xsd:element name="contact" type="contactType"/>
      <xsd:complexType name="contactType">
       <xsd:attribute name="name" type="xsd:string" use="required"/>
       <xsd:attribute name="telephone" type="xsd:string" use="required"/>
       <xsd:attribute name="email" type="xsd:string" use="required"/>
     </xsd:complexType>
    </xsd:schema>   

The command line tool xjc runs the JAXB compiler. To run the JAXB compiler against our schema, we run the following command:

   $xjc course-booking.xsd -p nz.co.equinox.training.domain.booking -d src/generated  

This will generate a set of Java classes annotated with JAXB 2.0 annotations. Some of the more useful options are described here:

  • -d <dir>: Place the generated files into this directory.
  • -p <package>: Place the generated files in this package.
  • -nv: Don't perform strict validation of the input schema.
  • -httpproxy <proxy>: Use this if you are behind a proxy. Takes the format [user[:password]@]proxyHost[:proxyPort].
  • -classpath <arg>: Specify the classpath, if necessary.
  • -readOnly: Generates read-only source code files, if your OS supports this.

There is also an equivalent ant task, which makes it quite easy to integrate into an Ant or Maven-based build process.

The list of generated classes is shown here:

  CompanyType.java
   ContactType.java
   CourseBooking.java
   ObjectFactory.java
   StudentType.java

Users of previous versions of JAXB may notice that this is a slick set of annotated and fully documented Java classes, rather than the more cumbersome set of interfaces and implementations of previous versions. Thus, we have less generated classes, and lighter and more elegant code. And, as you will see in the next section, manipulating these classes is easy.

Unmarshalling an XML document

Unmarshalling is the process of converting an XML document into a corresponding set of Java objects. Unmarshalling in JAXB 2.0 is easy. First, you create a JAXBContext context object. The context object is the starting point for marshalling, unmarshalling, and validation operations. Here you specify the Java package containing your JAXB-mapped classes:

       JAXBContext jaxbContext 
            = JAXBContext.newInstance
              ("nz.co.equinox.training.domain.booking");

To unmarshall an XML document, you create an Unmarshaller from the context, as shown here:

       Unmarshaller unmarshaller = 
        jaxbContext.createUnmarshaller();

The unmarshaller can process XML data from a wide variety of data sources: files, input streams, URLs, DOM objects, SAX parsers, and more. Here we provide a simple File object pointing to our XML document. The unmarshaller returns a typed JAXBElement, from which we can obtain our unmarshalled object by using the getValue() method:

 

JAXBElement<CourseBooking> bookingElement = (JAXBElement<CourseBooking>) unmarshaller.unmarshal( new File("src/test/resources/xml/booking.xml"));

CourseBooking booking = bookingElement.getValue();

Document validation

Document validation is the process of ensuring your XML document corresponds to the definition given in the corresponding XML schema. It is an important aspect of any project involving XML exchanges, especially if the XML comes from other systems. Document validation in JAXB 2.0 is easier and more flexible than in previous versions. You can simply attach a ValidatonEventHandler to the unmarshaller before unmarshalling the XML document, as shown here:

       unmarshaller.setEventHandler(new BookingValidationEventHandler());

A validation event handler implements the ValidationEventHandler interface and the handleEvent() method, as shown here:

 

public class BookingValidationEventHandler implements ValidationEventHandler{

public boolean handleEvent(ValidationEvent ve) {

if (ve.getSeverity()==ValidationEvent.FATAL_ERROR || ve .getSeverity()==ValidationEvent.ERROR){ ValidationEventLocator locator = ve.getLocator(); //Print message from valdation event System.out.println("Invalid booking document: " + locator.getURL()); System.out.println("Error: " + ve.getMessage()); //Output line and column number System.out.println("Error at column " + locator.getColumnNumber() + ", line " + locator.getLineNumber()); } return true; } }

Here we just print details of the error, but in a real application, some less trivial treatment might be appropriate. In some cases, you may even consider that the validation error is not a show-stopper and that it will not block the processing. By returning true, you tell the unmarshaller to continue the unmarshalling process: false would terminate the process with an appropriate exception.

Marshalling a document

Marshalling involves transforming your Java classes into XML format. In JAXB 2.0, creating and manipulating these Java classes is simple. In most cases, you can just treat them like ordinary Java classes, as shown here:

       CourseBooking booking = new CourseBooking();
        booking.setCourseReference("UML-101");
        booking.setTotalPrice(new BigDecimal(10000));
...

Note that you can still use the ObjectFactory class similarly to how you used it in JAXB 1.0, as shown in the following listing. However, unlike JAXB 1.0, there are no interfaces or implementation classes: all domain objects are just annotated JavaBeans components.

       ObjectFactory factory = new ObjectFactory();
        CourseBooking booking = factory.createCourseBooking();
...

Although most XML data types map directly to normal Java classes, some special treatment is needed for certain data types, such as dates. In these cases, you must use the DatatypeFactory, as shown here:

       DatatypeFactory datatypes = DatatypeFactory.newInstance();
        booking.setCourseDate(datatypes.newXMLGregorianCalendarDate(2006,06,15,0));

Once your domain object is initialized, use the JAXB context to create a Marshaller object and a typed JAXBElement. Creating the marshaller is simple:

       Marshaller marshaller = jaxbContext.createMarshaller();

Next, you create a JAXBElement object that encapsulates your domain object. The typed JAXBElement corresponds to the root element complexType of your XML document. Then use the generated ObjectFactory class as follows:

       JAXBElement<CourseBooking> bookingElement = (new ObjectFactory()).createBooking(booking);

In this example, we set a property so that the output will be formatted for human use and then write to standard output:

       marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal( bookingElement, System.out );

A full code sample is shown here:

 

JAXBContext jaxbContext = JAXBContext.newInstance("nz.co.equinox.training.domain.booking");

CourseBooking booking = new CourseBooking(); booking.setCourseReference("UML-101"); booking.setTotalPrice(new BigDecimal(10000)); booking.setInvoiceReference("123456"); DatatypeFactory datatypes = DatatypeFactory.newInstance(); booking.setCourseDate(datatypes.newXMLGregorianCalendarDate(2006,06,15,0)); booking.setTotalPrice(new BigDecimal(10000)); booking.setInvoiceReference("123456"); booking.getStudent().add(new StudentType()); booking.getStudent().get(0).setFirstName("John"); booking.getStudent().get(0).setSurname("Smith"); booking.setCompany(new CompanyType()); booking.getCompany().setName("Clients inc."); booking.getCompany().setContact(new ContactType()); booking.getCompany().getContact().setName("Paul"); booking.getCompany().getContact().setEmail("paul@clients.inc"); booking.getCompany().getContact().setTelephone("12345678"); booking.getCompany().setAddress("10 client street");

// Marshal to System.out Marshaller marshaller = jaxbContext.createMarshaller(); JAXBElement<CourseBooking> bookingElement = (new ObjectFactory()).createBooking(booking); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

marshaller.marshal( bookingElement, System.out );

Running this code will generate something like this:

1 2 Page
Recommended
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more