Exchanging Data With XML and JAXB, Part 2

by Jennie Hall

In Exchanging Data With XML and JAXB, Part 1, we saw how the Java Architecture for XML Binding (JAXB) expedites the exchange of data between systems. With JAXB 2.0's annotation support, generating XML data for a business partner is as easy as annotating your existing object model. 

In this tip, you'll learn how JAXB's binding customization features can facilitate XML processing for the recipient of data as well as for the sender. Binding customization offers a level of control over the characteristics of the Java classes generated by the JAXB schema compiler. This allows you to work with an object model that makes sense for your business domain even when processing XML instance documents based on a schema that is not of your own design.

For a brief overview of the Java Architecture for XML Binding (JAXB), see the "What's JAXB?" section of Exchanging Data With XML and JAXB, Part 1.

The Sample Application

Let's continue with our scenario from Part 1.A veterinary office, NiceVet, wants to send upcoming appointment reminders and pet birthday cards to its clients. NiceVet contracts with a service provider, WePrintStuff, to do the printing and mailing. In Part 1, we assembled some data for WePrintStuff by annotating NiceVet's existing object model and then using JAXB to marshal application data from Java objects to an XML instance document. We also used JAXB's schema generator to produce a corresponding schema from the annotated Java classes.

On the receiving end, WePrintStuff will run the JAXB schema compiler against the source schema to generate the schema-derived classes. JAXB will create instances of these classes as it unmarshals NiceVet's data from the XML instance document and binds it into a Java content tree. In some cases, however, the default manner in which JAXB generates the schema-derived classes and binds the data is not exactly what is needed. JAXB's binding customization features provide us with some nice ways to handle many of these cases. We'll take a look at some of them in the next section.

Customizing the Source Schema

We can make some customizations right away to make life easier at WePrintStuff. For example, we would like to work with meaningful package and class names and avoid any naming collisions. To accomplish this, we can add custom binding declarations to the source schema itself -- this is known as inline customization.

Here are the first few lines of the annotated schema:

	<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
	<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"
				 <!-- JAXB namespace declaration required for inline customization -->
	                         xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
	                         xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
	                         jxb:extensionBindingPrefixes="xjc"
				 <!-- JAXB version number required for inline customization -->
	                         jxb:version="2.0">
	    <xs:annotation>
	        <xs:appinfo>
                    <!-- All collections in this schema will use implementation class java.util.ArrayList. -->
	            <jxb:globalBindings collectionType="java.util.ArrayList"/>
	            <jxb:schemaBindings>
                        <!-- Specify the package for the generated classes; avoid naming collisions. -->
	                <jxb:package name="weprintstuff.generated">
                            <!-- Specify some Javadoc that describes our package and its uses. -->
	                    <jxb:javadoc><![CDATA[<body> The package weprintstuff.generated contains the
                                 schema-derived classes that make up the object model used to process
                                 print client orders.</body>]]>
	                    </jxb:javadoc>
	                </jxb:package>
	            </jxb:schemaBindings>
	        </xs:appinfo>
	    </xs:annotation>

To enable inline customization, we must include a JAXB namespace declaration and the JAXB version number. The JAXB namespace prefix we'll use in our sample application is jxb:. We can override the default JAXB bindings at different scopes. Lower-level, more fine-grained scopes inherit declarations made at higher-level scopes, but binding declarations made at lower-level scopes can override these inherited declarations. The scopes in order from highest to lowest level are global, schema, definition, and component.

In the preceding example, we've made a declaration at global scope that specifies that all collections in this schema will use the implementation class java.util.ArrayList. If our source schema imported another schema, this declaration would apply to the second schema as well. There can be only one <globalBindings> declaration per schema. This declaration is valid in the top-level schema only.

At schema scope, we've specified the package into which the JAXB schema compiler will generate the schema-derived classes. We've also included some package-level Javadoc that describes our package and its uses. Note that we've wrapped our custom binding declarations in <annotation><appinfo> tags.

Now that we have the package we want, let's see if we can get some class names that better reflect Java naming conventions and WePrintStuff's business domain. The default bindings for the original schema would generate classes with names like ClassAType, ClassBType, and so on.This is not how we typically name classes in the Java programming language, so let's fix it:

    <xs:element name="printOrder" type="PrintOrderType"/>
    <xs:complexType name="PrintOrderType">
        <xs:annotation>
            <xs:appinfo>
                <!-- Name the generated class PrintOrder rather than PrintOrderType. -->
                <jxb:class name="PrintOrder">
                    <!-- Provide some Javadoc for PrintOrder. -->
                    <jxb:javadoc><![CDATA[<code>PrintOrder</code> javadoc goes here]]>
                    </jxb:javadoc>
                </jxb:class>
            </xs:appinfo>
        </xs:annotation>
        <xs:sequence>
            <xs:element name="notifications" type="notificationsType" minOccurs="0">
                <xs:annotation>
                    <xs:appinfo>
                        <!-- PrintOrder's notifications property becomes printItems. -->
                        <jxb:property name="printItems"/>
                    </xs:appinfo>
                </xs:annotation>
            </xs:element>
        </xs:sequence>
		...
    </xs:complexType>
    <xs:complexType name="notificationsType">
        <xs:annotation>
            <xs:appinfo>
                <!-- Name the generated class PrintItems rather than NotificationsType. -->
                <jxb:class name="printItems" />
            </xs:appinfo>
        </xs:annotation>
			...
    </xs:complexType>
		

PrintOrderType becomes PrintOrder, NotificationsType becomes PrintItems, and so on. Dealing with class names is straightforward, but unfortunately we're stuck with a class structure that is less than ideal. There are too many wrappers around the data, which results in a long chain of method calls. Here's what the calls would have looked like with classes derived from the original schema. As you can see, methods with singular method names return collections, which is misleading.

	List<AppointmentType> appointments = printOrder.getNotifications().getAppointments().getAppointment();
	List<BirthdayType> birthdays = printOrder.getNotifications().getBirthdays().getBirthday();

The following improved calls show the new names, which say what they mean and are more relevant to WePrintStuff's business domain.

	List<Appointment> appointments = printOrder.getPrintItems().getAppointmentHolder().getAppointments();
	List<Birthday> birthdays = printOrder.getPrintItem().getBirthdayHolder().getBirthdays();

I tried to solve some of the class structure issues by using the enhanced <jxb:javaType> customization (<xjc:javaType>) and an adapter class derived from XmlAdapter, but JAXB currently does not support this customization for XML complex types. This issue is documented in the JAXB issue tracker as issue number 209. Meanwhile, we'll look at an example of the enhanced <jxb:javaType> customization used with an XML simple type in the upcoming paragraphs.

Moving down the source schema, we see that each print order received will have an ID of type long. We know, however, that WePrintStuff employs a natural key strategy in its persistent store. For print-order records, the key consists of a client name and an order ID. WePrintStuff uses the class PrintOrderKey to encapsulate this key information. We'll apply a binding customization that causes the JAXB schema compiler to use an adapter class, IdAdapter, to replace the original print-order ID with WePrintStuff's PrintOrderKey class. Take a look:

   <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:annotation>
                <xs:appinfo>
                    <!-- Use an XmlAdapter-based adapter to create WePrintStuff's PrintOrderKey class. -->
                    <!-- Specify the name orderKey for this property. -->
                    <jxb:property name="orderKey">
                        <jxb:baseType>
                            <!-- Specify weprintstuff.print.PrintOrderKey as the type of the orderKey property. -->
                            <!-- Specify the adapter class that will map the schema type to the Java type. -->
                            <xjc:javaType name="weprintstuff.print.PrintOrderKey"
                              adapter="weprintstuff.print.IdAdapter"/>
                        </jxb:baseType>
                    </jxb:property>
                </xs:appinfo>
            </xs:annotation>
        </xs:attribute>
    </xs:complexType>

We've changed the name of the print-order property to orderKey and specified the types of the adapter and the order key. During the unmarshalling process, JAXB calls IdAdapter's unmarshal() method, which receives the value of id and incorporates it into a new PrintOrderKey containing the order ID and client name. Here's the code for IdAdapter:

	public class IdAdapter extends XmlAdapter<String, PrintOrderKey> {

	    // When marshalling a Java content tree to an XML instance document,
	    // move from the type that we work with in Java (PrintOrderKey)
	    // to the type that JAXB understands.
	    public String marshal(PrintOrderKey key) throws Exception {
	        return key.getOrderId().toString();
	    }

	    // When unmarshalling an XML instance document to a Java content tree,
	    // move from the type that JAXB understands (String) to the type
	    // we want to work with in Java technology.
	    public PrintOrderKey unmarshal(String id) throws Exception {
	        // WePrintStuff uses natural keys. Add client name and
	        // convert String ID to required Long.
	        return new PrintOrderKey("NICEVET", new Long(id));
	    }
	}

The <xjc:javaType> customization we've just discussed is an example of a vendor extension. The JAXB reference implementation (RI) offers additional customizations that are not part of the JAXB specification. To use these extensions, we must include a declaration for the JAXB RI vendor extension namespace and specify a namespace prefix:

	<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"
	                         xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
                                 <!-- JAXB RI vendor extension namespace declaration is required. -->
	                         xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
                                 <!-- JAXB RI vendor extension namespace prefix is required. -->
                                 jxb:extensionBindingPrefixes="xjc"
	                         jxb:version="2.0">

Finally, vendor extensions require that we run the schema compiler with the -extension switch. For an example of the standard <jxb:javaType> customization, which is not a vendor extension, check out niceVet.xsd and weprintstuff.print.CustomDataTypeConverter. These files are included with the sample code that comes with this tech tip. In our example, we've used the <jxb:javaType> customization to convert XML dateTime types to nicely formatted String dates ready for output.

Things are looking better, but we'd also like to handle the printing of appointment reminders and birthday cards as polymorphically as we can. Take a look at the following:

   <xs:complexType name="AppointmentType">
        <xs:annotation>
            <xs:appinfo>
                <!-- Name has generated class Appointment rather than AppointmentType. -->
                <jxb:class name="Appointment">
                    <jxb:javadoc><![CDATA[<code>Appointment</code> javadoc goes here]]>
                    </jxb:javadoc>
                </jxb:class>
            </xs:appinfo>
        </xs:annotation>
        <xs:complexContent>
            <!-- XML Schema extension element causes AppointmentType to derive from printItemType, a type we've added to the schema. -->
            <xs:extension base="printItemType">
                <xs:sequence>
                   ...
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>
        ...
    <!-- Add this complex type to the schema so that Appointment class now derives from PrintItem, a base class that we've implemented. -->
    <xs:complexType name="printItemType">
        <xs:annotation>
            <xs:appinfo>
                <!-- Due to this customization, JAXB will not generate a class for printItemType; it will use the PrintItem class that we've written. -->
                <jxb:class ref="weprintstuff.print.PrintItem"/>
            </xs:appinfo>
        </xs:annotation>
    </xs:complexType>

Fortunately for us, we can modify the structure of the source schema somewhat while retaining the schema's ability to validate the same set of XML instance documents. In this case, we have added a totally new complex type, printItemType, to the schema. Then we modified the complex type AppointmentType with the XML Schema extension element, causing AppointmentType to derive from printItemType.

At this point, we applied a JAXB customization to printItemType that directs the schema compiler to use the specified class that we've written rather than generating one. Because we've implemented PrintItem ourselves, we can include behavior: specifically, a print() method that allows us to handle subclasses, such as Appointment and Birthday, in a more polymorphic manner. Here's the code for PrintItem:

	package weprintstuff.print;

	import java.util.\*;

	public abstract class PrintItem {

	    private static final Map<String,Printer> printers;

	    static {
	        printers = new HashMap<String,Printer>();
	        printers.put("weprintstuff.generated.Birthday", new BirthdayPrinter());
	        printers.put("weprintstuff.generated.Appointment", new AppointmentPrinter());
	    }

	    public void print() {
	        Printer p = this.getPrinter();
	        p.print(this);
	    }

	    private Printer getPrinter() {
	        return printers.get(this.getClass().getName());
	    }
	}

The Printer interface has a single method:

    public void print(PrintItem item);

Although we're still hampered by the structure of the schema-derived classes as discussed earlier, the code to print the items in the print order is now pretty clean. Here's an excerpt from our main() method:

	// Unmarshal the data in the XML instance document to a Java content tree
	// made up of instances of the schema-derived classes.
	JAXBElement printOrderElement = (JAXBElement)unmarshaller.unmarshal(new FileInputStream(input));
	PrintOrder printOrder = (PrintOrder)printOrderElement.getValue();

	List<Appointment> appointments = printOrder.getPrintItems().getAppointmentHolder().getAppointments();
	for (Appointment a : appointments) {
		a.print();
	}

	List<Birthday> birthdays = printOrder.getPrintItems().getBirthdayHolder().getBirthdays();
	for (Birthday b : birthdays) {
		b.print();
	}

Another nice customization feature that JAXB provides is the ability to map XML simple types to typesafe enumerations. This is convenient because WePrintStuff currently has printing templates for certain types of appointments only. We'll modify the schema to include the supported appointment types and add a customization that causes the schema compiler to map the elements of ApptType to a Java typesafe enum class. XML instance documents with appointment types that WePrintStuff does not support will be rejected:

    <xs:simpleType name="ApptType">
        <xs:annotation>
            <xs:appinfo>
                <!-- Map the elements of this simple type to a Java typesafe enum class. -->
                <jxb:typesafeEnumClass/>
            </xs:appinfo>
        </xs:annotation>
        <!-- Use XML Schema elements restriction and enumeration to define the supported appointment types. -->
        <xs:restriction base="xs:string">
            <xs:enumeration value="Yearly Checkup"/>
            <xs:enumeration value="Well Mom Exam"/>
            <xs:enumeration value="Teeth Cleaning"/>
            <xs:enumeration value="Vaccination"/>
            <xs:enumeration value="Senior Pet Checkup"/>
        </xs:restriction>
    </xs:simpleType>

Here is the resultant typesafe enum class, ApptType:

	package weprintstuff.generated;

	import javax.xml.bind.annotation.XmlEnum;
	import javax.xml.bind.annotation.XmlEnumValue;
	import javax.xml.bind.annotation.XmlType;

	@XmlType(name = "ApptType")
	@XmlEnum
	public enum ApptType {

	    @XmlEnumValue("Yearly Checkup")
	    YEARLY_CHECKUP("Yearly Checkup"),
	    @XmlEnumValue("Well Mom Exam")
	    WELL_MOM_EXAM("Well Mom Exam"),
	    @XmlEnumValue("Teeth Cleaning")
	    TEETH_CLEANING("Teeth Cleaning"),
	    @XmlEnumValue("Vaccination")
	    VACCINATION("Vaccination"),
	    @XmlEnumValue("Senior Pet Checkup")
	    SENIOR_PET_CHECKUP("Senior Pet Checkup");
	    private final String value;

	    ApptType(String v) {
	        value = v;
	    }

	    public String value() {
	        return value;
	    }

	    public static ApptType fromValue(String v) {
	        for (ApptType c: ApptType.values()) {
	            if (c.value.equals(v)) {
	                return c;
	            }
	        }
	        throw new IllegalArgumentException(v);
	    }

	}

So far we've made what are known as inline binding customizations, but JAXB also accepts customizations in the form of one or more external binding customization files. Inline and external customizations can be used in combination, but they cannot be used together on the same element. External binding files are useful when you cannot modify a given schema or when you want to reuse customizations across schemas. For example, WePrintStuff has its own class for U.S. addresses, DomesticAddress, and WePrintStuff would like to use this class to represent the concept of an address throughout the system.

Here's the external binding file, binding.xjb:

	<!-- XML Schema namespace is required, as are the JAXB namespace and version. -->
	<jxb:bindings version="2.0"
	               xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
	               xmlns:xs="http://www.w3.org/2001/XMLSchema">

	  <!-- Specify the schema name and root schema node. -->
	  <jxb:bindings schemaLocation="niceVet.xsd" node="/xs:schema">

	    <!-- Specify the schema node to which this customization should apply with an XPath expression. -->
	    <jxb:bindings node="//xs:complexType[@name='AddressType']">
                <!-- Direct the schema compiler to use the DomesticAddress class rather than generating a class for complex type AddressType. -->
	        <jxb:class ref="weprintstuff.print.DomesticAddress"/>
	    </jxb:bindings> <!-- node="//xs:complexType[@name='AddressType']" -->

	  </jxb:bindings> <!-- schemaLocation="niceVet.xsd" node="/xs:schema" -->
	</jxb:bindings>

The binding customization file is just an ASCII text file. A valid binding file must specify the schema name and node. We identify nodes using XPath expressions. In the previous listing, we've applied a customization that directs the schema compiler to use WePrintStuff's DomesticAddress class rather than generating a class for the complex type AddressType. This is the same customization illustrated earlier in our PrintItem base class example, although in that situation, we declared the customization inline. The syntax for supplying a binding customization file to the schema compiler is the following:

	xjc -b bindings schema

We can apply multiple binding files to one schema, one binding file to multiple schemas, or multiple binding files to multiple schemas. Each binding file must have its own -b switch.

Generating the Schema-Derived Classes

We generate our schema-derived classes at the command line with the following command to the schema compiler, xjc:

	xjc -d ./src -b binding.xjb -extension niceVet.xsd

The schema compiler informs us that it has generated our classes with the following output:

	parsing a schema...
	compiling a schema...
	weprintstuff\\generated\\Adapter1.java
	weprintstuff\\generated\\Adapter2.java
	weprintstuff\\generated\\Appointment.java
	weprintstuff\\generated\\ApptType.java
	weprintstuff\\generated\\Birthday.java
	weprintstuff\\generated\\ObjectFactory.java
	weprintstuff\\generated\\Owner.java
	weprintstuff\\generated\\Pet.java
	weprintstuff\\generated\\PrintItems.java
	weprintstuff\\generated\\PrintOrder.java
	weprintstuff\\generated\\package.html

The JAXB schema compiler has a number of options. For example, it will mark generated classes as read only in response to the -readOnly switch. Invoke xjc with no options or with the -help switch for more information.

The Payoff

We've modified our source schema, made our customizations, and generated our schema-derived classes. All that's left to do is run our printing program, let JAXB bind the XML data supplied by NiceVet, and print out the appointment reminders and birthday cards. We'll take a look at our XML instance document:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<printOrder id="1219271208522">
    <notifications>
        <appointments>
            <appointment>
                <apptType>Yearly Checkup</apptType>
                <apptDate>2008-09-15T00:00:00-07:00</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>2008-09-12T00:00:00-07:00</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>2000-09-07T00:00:00-07:00</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>

And here's another look at the main() method:

	// The XML instance document received from NiceVet contains NiceVet's print order.
	File input = new File("niceVet.xml");

	// Create a JAXBContext for the weprintstuff.generated package.
	JAXBContext ctx = JAXBContext.newInstance("weprintstuff.generated");

	// Create an unmarshaller.
	Unmarshaller unmarshaller = ctx.createUnmarshaller();

	// Unmarshal the data in the XML instance document to a Java content tree made up of instances of the schema-derived classes.

	JAXBElement printOrderElement = (JAXBElement)unmarshaller.unmarshal(new FileInputStream(input));
	PrintOrder printOrder = (PrintOrder)printOrderElement.getValue();

	// Print out the print items.	
        List<Appointment> appointments = printOrder.getPrintItems().getAppointmentHolder().getAppointments();
	for (Appointment a : appointments) {
		a.print();
	}

	List<Birthday> birthdays = printOrder.getPrintItems().getBirthdayHolder().getBirthdays();
	for (Birthday b : birthdays) {
		b.print();
	}

Check out our results: No one will ever miss an appointment or a birthday again.

	Hi Joe!
	Our records show that your dog Honcho has a Yearly Checkup appointment scheduled on 09/15/2008.
	Please call us 24 hours prior to your appointment if you need to reschedule.
	Sincerely, NiceVet

	Hi Missy! Our records show that your cat Miss Kitty has a Well Mom Exam appointment scheduled on 09/12/2008.
	Please call us 24 hours prior to your appointment if you need to reschedule.
	Sincerely, NiceVet

	Hi Violet!
	Our records show that your cat Tom will turn 7 years old on 09/07.
	Happy Birthday, Tom, from all of us at NiceVet!

Running the Sample Application

To run the sample application, download the sample code and unzip it. Navigate to the directory <sample-install-dir>/schema-to-java and generate the schema-derived classes as described in the section "Generating the Schema-Derived Classes."

Launch the NetBeans IDE 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 schema-to-java. Select the Open as Main Project check box. Click Open Project Folder. Right-click the schema-to-java project and select Run Project.

Conclusion

In Exchanging Data With XML and JAXB, Parts 1 and 2, we've learned how JAXB can facilitate the flow of data between business partners. With features that expedite XML processing on both ends of the exchange, JAXB can enable simpler, more productive integration between systems.

References and Resources

Sample code for this tip
Java EE 5 Tutorial
Kohsuke Kawaguchi's Blog - Kohsuke Kawaguchi is a staff engineer at Sun Microsystems, where he works on JAXB, among other projects.
JSR 222: JAXB Specification

About the Author

Jennie Hall is a lead developer working in the financial sector.

Comments:

awesome!

Posted by bob smith on September 18, 2008 at 03:31 AM PDT #

These tips are really useful. Thanks,

Posted by Mike Jones on September 23, 2008 at 10:11 AM PDT #

profe la verdad la informacion creo que no esta tan clara. Es mucha informacion y que hay que hacer con eso

Posted by guest on September 27, 2008 at 08:26 AM PDT #

i just won't to get started

Posted by wolf wayne robinson on October 01, 2008 at 03:24 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