Using JAXB






Using JAXB

The APIs for JAXB 1.0 and JAXB 2.0 are relatively similar even though the implementations are quite different. At the core of both APIs are interfaces called Marshaller and Unmarshaller, both in the javax.xml.bind package. A factory class called JAXBContext (also in javax.xml.bind) exists to create instances of these interfaces. Figure contains a UML model for the core JAXB 1.0 API.

JAXB 1.0 core API


The significant changes between the JAXB 1.0 versions of these interfaces and the JAXB 2.0 versions relate to the use of JAXP validation. Specifically, the setValidating( ) and isValidating( ) methods of the Unmarshaller interface are deprecated. Instead, the Marshaller and Unmarshaller interfaces now have methods called setSchema( ) and getSchema( ), which deal with instances of javax.xml.validation.Schema , discussed in Chapter 7. In addition, as mentioned above, both Marshaller and Unmarshaller now have methods that accept the reader and writer interfaces from the StAX API discussed in Chapter 8.

JAXBContext

To obtain an instance of the Marshaller, Unmarshaller, or Validator interfaces, you first need to obtain an instance of JAXBContext. To do so, call one of JAXBContext's static newInstance( ) methods. In JAXB 1.0, both newInstance( ) methods accept a colon-separated list of package names. The second newInstance( ) method also accepts a ClassLoader object that will be used to load the classes in those packages. The newInstance( ) method searches for classes within these packages to create the context paththe list of classes that instances created by the JAXBContext object is able to marshall, unmarshall, and validate. In JAXB 2.0, there are additional newInstance( ) methods that allow you to pass in a list of Class objects, using the Java 5 varargs language features. When passing one or more classes to newInstance( ), the resulting JAXBContext's context path contains both of the classes passed to newInstance( ) and any dependent classes. Thus, you generally only need to pass one or two top-level classes, and the dependent classes will automatically be added to the context path.

If we had compiled a schema into the package javaxml3.ch11.person1, this line would create the appropriate JAXBContext object in both JAXB 1.0 and JAXB 2.0:

JAXBContext context = JAXBContext.newInstance("javaxml3.ch11.person1");

And in JAXB 2.0, we could also do this, assuming the compiled schema has a root element called Person:

JAXBContext context = JAXBContext.newInstance(javaxml3.ch11.person.Person.class);

One you have a JAXBContext object, it's easy to create Marshaller, Unmarshaller, and Validator instances:

Marshaller marshaller = context.createMarshaller(  );
Unmarshaller unmarshaller = context.createUnmarshaller(  );
Validator validator = context.createValidator(  );

But before we use any of those interfaces, we need some classes to work with. So first let's look at compiling a schema.

Compiling a Schema

As noted above, the JAXB specification does not specify how a developer executes a schema compiler, only how the compilation is done. Most implementations provide a command-line application, an Ant task, or both. The reference implementations provide both a command-line application named xjc and an Ant task in the class com.sun.tools.xjc.XJCTask.

I cover the command-line application below and most of this information also applies to the Ant task. For the details on executing the Ant task, check the documentation that comes with your JAXB implementation.


Compiling with xjc

The command-line application can be run on Windows systems, using the batch file xjc.bat, and on Unix-like systems (including Linux and Mac OS X), using the shell script xjc.sh. Both of these files are in the bin directory of the JAXB distribution directory. In general, to run either script, you must have the JAVA_HOME environment variable set to the location of your JVM. See your operating system documentation for more information on how to set environment variables.

The only required parameter to xjc is the schema. This could be a local file path or a URL. There are a number of optional parameters that can be included on the command line, listed in Figure.

-nv Turn off strict validation of the input schema.
-extension Permit vendor extensions to the JAXB specification.
-b <file/directory> Specify external bindings file or files. If a directory name is passed, all files with the xjb extension will be included. Directories are only supported by JAXB 2.0 RI. Bindings files are discussed later in the section "Schema Compilation Customization."
-d <directory> Output directory for generated files.
-p <package> Specify the package for generated classes and interfaces.
-host <proxyHost> Host name for HTTP proxy, if any. JAXB 1.0 RI only.
-port <proxyPort> Port number for HTTP proxy, if any. JAXB 1.0 RI only.
-httpproxy <proxy> HTTP proxy information in the format [user[:password]@]proxyHost[:proxyPort]. JAXB 2.0 RI only.
-classpath <classpath> Additional classpath entries.
-catalog <file> Catalog files for external entity references.
-readOnly If specified, generated files will be created in read-only mode.
-use-runtime <package> Do not generate the impl.runtime package and instead reference these classes in a different package. JAXB 1.0 RI only. See the section "JAXB 1.0 generated files" later in the chapter for more details.
-npa Suppresses generation of package-level Java annotations. JAXB 2.0 RI only.
-no-header Suppresses inclusion of a header including the timestamp in all generated files. JAXB 2.0 RI only.
-xmlschema Forces the treatment of the input file as a W3C XML Schema. This is the default.
-relaxng Treats the input file as a Relax NG schema. This is an unsupported option.
-relaxng-compact Treats the input file as a Relax NG compact syntax schema. This is an unsupported option and in JAXB 2.0 RI only.
-dtd Treats the input file as a DTD. This is an unsupported option.
-wsdl Treats the input file as a WSDL and searches for schemas within it. This is an unsupported option in JAXB 2.0 RI only.
-verbose Turns on extra debug logging. JAXB 2.0 RI only.
-quiet Suppresses compiler output.
-help Displays a help message containing a list of the command-line options.
-version Displays version information about the compiler.


By default, the compiler will create a package name for the generated interfaces and classes based on the URI of the target namespace. A namespace URI of http://www.example.com/person will result in a package name of com.example.person. If there is no target namespace, such as in Figure, then the package name generated is used. To override this (which I highly recommend), use the -p command-line option.

A schema with no target namespace

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="person">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="firstName" type="xs:string"/>
                <xs:element name="lastName" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

To compile the schema in Figure in a file called person.xsd and have the compiled code go in the package com.example.person, run the command:

xjc p com.example.person person.xsd

This will create output files in the current directory. To specify the output directory (which must exist already), use the -d option:

xjc p com.example.person d src person.xsd

Although the command is the same for both JAXB 1.0 RI and JAXB 2.0 RI (with a few options only available for one or the other), the files generated are dramatically different as you can see by comparing the output of both commands in Figures 11-12 and 11-13.

JAXB 1.0 compiler output


JAXB 2.0 compiler output


Before you start counting, I'll save you the trouble: the JAXB 1.0 compiler outputs 38 files compared with JAXB 2.0's two files. Looking more closely at what those files contain explains why this difference is so significant.

JAXB 1.0-generated files

First, let's look at the two interfaces the JAXB 1.0 compiler created in our main package: Person and PersonType. The JAXB 1.0 compiler will create an interface for every element and complex type declared in the schema. If an element contains an anonymous complex type, such as in Figure, the compiler will create one interface to represent the complex type and another to represent the element. If, instead of the anonymous complex type, we had a named complex type, such as in Figure, the PersonType interface would not be created. Instead the interface for the named complex type would be generated. In the case of Figure, this interface's name would be Atype.

Schema without anonymous complex type

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="person" type="atype"/>
    <xs:complexType name="atype">
        <xs:sequence>
            <xs:element name="firstName" type="xs:string"/>
            <xs:element name="lastName" type="xs:string"/>     
        </xs:sequence>
    </xs:complexType>
</xs:schema>

he interfaces themselves are fairly simple. The PersonType interface defines getter and setter methods for the firstName and lastName child elements:

package com.example.person;

public interface PersonType {
    java.lang.String getFirstName(  );
    void setFirstName(java.lang.String value);
    java.lang.String getLastName(  );
    void setLastName(java.lang.String value);

}

The JAXB compiler includes Javadoc comments in the generated code, but those have been removed from these examples.


The Person interface merely extends the PersonType interface and the javax.xml.bind.Element marker interface to mark that this interface represents an element:

package com.example.person;

public interface Person
    extends javax.xml.bind.Element, com.example.person.PersonType{

}

Also in the com.example.person package is the ObjectFactory class, instances of which can be used to create implementations of the Person and PersonType interfaces:

ObjectFactory factory = new ObjectFactory(  );
Person person = factory.createPerson(  );
// or
PersonType personType = factory.createPersonType(  );

The compiler also creates the file jaxb.properties and bgm.ser, both of which are used by the JAXBContext class to create Marshaller, Unmarshaller, and Validator objects.

Inside com.example.person, the JAXB compiler has created an impl package containing implementations of the generated Person and PersonType interfaces. It is instances of these classes (PersonImpl and PersonTypeImpl) that the ObjectFactory class will return. These implementation classes are fairly longin this case, PersonImpl and PersonTypeImpl generated by the JAXB 1.0 reference implementation combined come to about 500 lines. Most of the generated code is fairly boilerplate with the exception of the createRawValidator( ) method, which defines a schema fragment using a byte stream. Clearly if you need to make changes to your schema, you should make them to your schema file and recompile it.

Finally, there's the package com.example.person.impl.runtime. This contains classes and interfaces that are used by the JAXB runtime. If you have multiple packages of JAXB-generated code, you can avoid repeatedly duplicating these files by passing the -use-runtime command-line option with the name of a package that already contains these classes and interfaces.

JAXB 2.0-generated files

Unlike the JAXB 1.0 compiler, the JAXB 2.0 compiler produces a concrete class called Person in our target package. The generated class is an annotated POJO, seen in Figure.

JAXB 2.0 generated element class

package com.example.person;

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 = {
    "firstName",
    "lastName"
})
@XmlRootElement(name = "person")
public class Person {

    @XmlElement(required = true)
    protected String firstName;
    @XmlElement(required = true)
    protected String lastName;

    public String getFirstName(  ) { return firstName; }

    public void setFirstName(String value) { this.firstName = value; }

    public String getLastName(  ) { return lastName; }

    public void setLastName(String value) { this.lastName = value; }
}

As you can see, the bulk of this class is basic JavaBean code: two fields named firstName and lastName along with appropriate getters and setters for these fields. All of the metadata JAXB needs to marshall and unmarshall instances of this class are contained within the annotations. And unlike the generated classes from JAXB 1.0, this is actually code you could write yourself. Although you don't need it in this case, the ObjectFactory class is generated and contained in Figure. Instead of using createPerson( ) on an ObjectFactory instance, it's generally more natural to use the public constructor of the Person class. Having the ObjectFactory class is still useful as it enables you to use code written against JAXB 1.0 without modification.

JAXB 2.0 generated ObjectFactory

package com.example.person;

import javax.xml.bind.annotation.XmlRegistry;

@XmlRegistry
public class ObjectFactory {

    public ObjectFactory(  ) {
    }

    public Person createPerson(  ) {
        return new Person(  );
    }

}

Let's take a look at what these annotations do and how you can use them directly.

JAXB Annotations

One of the key new features in JAXB 2.0 is the ability to use Java annotations to make any class able to participate in marshalling and unmarshalling. We've already seen some of these annotations in Examples 11-6 and 11-7. These annotations aren't just for classes generated by the schema compiler. It's possible to use them to annotate any class to enable marshalling and unmarshalling of objects of that class.

If you're not familiar with Java annotations, now would be a good time to stop reading this book and go read Java in a Nutshell by David Flanagan (O'Reilly) or Java 5.0 Tiger: A Developer's Notebook by David Flanagan and Brett McLaughlin (O'Reilly).


Class-level annotations

Annotations can be attached to a package, a class, a field, or a method. Figure contains both annotations to both the class and its fields. Again, the class-level annotations in the example were:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "firstName",
    "lastName"
})
@XmlRootElement(name = "person")
public class Person {

XmlRootElement

The order of annotations is not important, and in this case, the JAXB schema compiler has the most important annotation last. The annotation javax.xml.bind.annotation.XmlRootElement declares that the class that it annotates is usable as a root element in a marshalled XML document. In the example above, we set the name member of the annotation to person. This isn't actually necessary, as JAXB would have automatically lowercased the first letter of the class name. Still, it's a good idea to include the name member to ensure the XML element name would survive a renaming of the class. With XMLRootElement, you can also specify a namespace URI using the namespace member.

If you were only to specify the XMLRootElement annotation, JAXB would include every public JavaBean property of your object as a child element in the marshalled XML. In other words, the simple class in Figure could produce XML documents such as Figure, with only one annotation.

A simple Person class

package javaxml3;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(  )
public class Person {
    private String firstName; 
    private String lastName;

    public String getFirstName(  ) { return firstName; }

    public String getLastName(  ) { return lastName; }

    public void setFirstName(String s) { firstName = s; }

    public void setLastName(String s) { lastName = s; }

}

Person output XML

<?xml version="1.0" encoding="UTF-8"?>
<person>
    <firstName>Burt</firstName>
    <lastName>Arbuckle</lastName>
</person>

XmlAccessorType

You can use the javax.xml.bind.annotation.XmlAccessorType annotation to specify the default rule for child element marshalling. The member of this annotation is a value from the enumeration class javax.xml.bind.annotations.XmlAccessType, which has four possible values:


PUBLIC_MEMBER

This is the default value, whose behavior we saw above. With this value, the marshaller will marshall every public getter and setter pair. To specify that a JavaBean property should not be included in the marshalled output, either the getter or the setter must be annotated with the javax.xml.bind.annotation.XmlTransient annotation.


FIELD

With this value, the marshaller will marshall every nonstatic and nontransient field in the class. As above, if a field should not be marshalled, it must be annotated with XmlTransient. Likewise, if a getter/setter pair should be marshalled, it must be explicitly annotated with another JAXB annotation.


PROPERTY

This value is similar to PUBLIC_MEMBER, but all getter/setter pairs are marshalled, including those that have private, protected, or package-only access.


NONE

If NONE is the value set as the member of XmlAccessorType, no child elements will be marshalled automatically. Any fields or properties that need to be marshalled must be explicitly annotated.

XmlType

Last among the class-level annotations from Figure is XmlType. This annotation allows you to specify more detailed information about the schema type that this class represents. In the case of Figure, we have an anonymous complex type (name=""), which is to be expected based on the schema. Like XmlRootElement, XmlType has a namespace member that can specify the namespace URI for the type. XmlType can also, as we saw in Figure, specify the order of children for marshalling. By default, child elements will be marshalled in alphabetical order. If you specify the order using XmlType's propOrder member as in Figure, the value of XmlAccessorType, if any, is ignored and only those properties listed in propOrder are included in the marshalled output.

Field-level annotations

Figure contained only a single field-level annotation:

@XmlElement(required = true)
protected String firstName;
@XmlElement(required = true)
protected String lastName;

XmlElement

As with XmlRootElement, discussed above, javax.xml.bind.annotation.XmlElement declares the existence of an XML element. But whereas XmlRootElement annotated classes, XmlElement is used to annotate JavaBean properties and fields. XmlElement has name and namespace members for overriding the default element name and namespace URI, respectively. It also has several members for declaring constraints on the element. These are:


required

As used above, indicates that the element is required.


nillable

An element that represents a null property will not be output if nillable is false (the default). However, if nillable is true, an empty element will be output with the attribute xsi:nil set to true.


defaultValue

Sets the default value for an element.

XmlAttribute

The schema in Figure did not contain any attributes, so our Java class didn't contain any uses of javax.xml.bind.annotation.XmlAttribute. XmlAttribute can be used in the same ways as XmlElement: annotating a field or a JavaBean property. A single field or property can either be marshalled as an element or an attribute, not both. This is illegal:

@XmlAttribute(name="valueAttribute")
@XmlElement(name="valueElement")
private String value;

However, it is completely legal to have an attribute and element with the same name. The XmlAttribute annotation, like XmlElement, has name, namespace, and required members.

Package-level annotations

Although it's possible, as I've mentioned, to assign namespace URIs to elements and attributes using the namespace member, if you're defining a schema, you need to declare the target namespace. With JAXB annotations, this is done with a package-level annotation called javax.xml.bind.annotation.XmlSchema. To create a package-level annotation, create a file called package-info.java and add a regular Java package declaration to it. Annotations can then be added to this file and they will apply to the package as a whole. For example, to set the target namespace to http://www.example.com/person and set the default element namespace qualification form to qualified, you can use the XmlSchema annotation like this:

@javax.xml.bind.annotation.XmlSchema(
    namespace = "http://www.example.com/person",
    elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED
)
package javaxml3;

You can also use XmlSchema to declare namespace prefixes:

@javax.xml.bind.annotation.XmlSchema(
    namespace = "http://www.example.com/person",
    elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED,
    xmlns = { 
        @javax.xml.bind.annotation.XmlNs(prefix = "p",
            namespaceURI="http://www.example.com/person")
    }
)
package javaxml3;

This does not necessarily mean that the prefix specified will be used in the marshalled output. The prefix associations defined with the XmlSchema annotation apply primarily to a schema generated from these classes.


This is only the beginning of what you can do with JAXB 2.0 annotations. There are numerous annotations not covered here. Please see the Javadocs for JAXB 2.0 (included in the distributions) and the JAXB 2.0 specification for more details on these additional annotations.

Schema generation

In JAXB 2.0, XML Schemas can be generated from annotated classes. Like schema compilation, the JAXB specification defines how a schema gets generated but not how a developer triggers the generation process. In the reference implementation, schema generation is done with a command-line application called schemagen. Like the xjc schema compiler, schemagen comes in both a Windows batch file and a Unix shell script. At a minimum, you must pass schemagen a list of source files from which to generate the schema. If these source files refer to other classes, the sources for those classes must either be explicitly provided on the command line or in a directory passed to schemagen with the -cp or -classpath command-line options. In general, it's easiest to provide a single source file containing a root element for your schema and pass the directory containing all of your source files as the classpath.

By default, schemagen will output the schema and compiled versions of the source files in the current directory. This can be overridden with the -d command-line option.

The reference implementation also provides an Ant task for schema generation. Its options are identical to the command-line version. See the JAXB reference implementation documentation for more details.

Marshalling

One you have generated classes from a schema, it is very simple to create and marshall an object:

package javaxml3;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import com.example.person.ObjectFactory;
import com.example.person.Person;

public class PersonMarshaller {

    public static void main(String[] args) throws JAXBException {
        ObjectFactory factory = new ObjectFactory(  );
        Person person = factory.createPerson(  );
        person.setFirstName("Alan");
        person.setLastName("Turing");

        JAXBContext context = JAXBContext.newInstance("com.example.person");
        Marshaller marshaller = context.createMarshaller(  );
        marshaller.marshal(person, System.out);
    }

}

Running this class produces the following output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person><firstName>Alan</firstName><lastName>Turing</lastName></person>

In addition to the java.io.OutputStream-accepting method used in the example above, there are marshal( ) methods that accept the following output mechanisms:

  • org.xml.sax.ContentHandler

  • org.w3c.dom.Node

  • javax.xml.transform.Result

  • javax.io.Writer

JAXB 2.0 adds two additional options:

  • javax.xml.stream.XMLStreamWriter

  • javax.xml.stream.XMLEventWriter

The only one of these that warrants explanation is org.w3c.dom.Node . This method will marshall the passed object and create DOM nodes as children of the passed Node object. This allows you to add the marshalled output as a fragment inside a preexisting DOM Document or Element object. As an example, this class:

package javaxml3;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.example.person.ObjectFactory;
import com.example.person.Person;

public class DOMMarshaller {

    public static void main(String[] args) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(  );
        DocumentBuilder builder = dbf.newDocumentBuilder(  );
        Document doc = builder.newDocument(  );
        Element people = doc.createElement("people");
        doc.appendChild(people);

        ObjectFactory factory = new ObjectFactory(  );
        Person person = factory.createPerson(  );
        person.setFirstName("Alan");
        person.setLastName("Turing");

        JAXBContext context = JAXBContext.newInstance("com.example.person");
        Marshaller marshaller = context.createMarshaller(  );
        marshaller.marshal(person, people);

        TransformerFactory tf = TransformerFactory.newInstance(  );
        tf.newTransformer(  ).transform(new DOMSource(doc),
                new StreamResult(System.out));

    }

}

Outputs the following XML document. The highlighted lines contain the elements created by the marshaller.

<?xml version="1.0" encoding="UTF-8"?>@XmlAccessorType(XmlAccessType.FIELD)
<people>
    <person>
        <firstName>Alan</firstName>
        <lastName>Turing</lastName>
    </person>
</people>

I have reformatted the output. The identity transform used to output the document puts all the content on one line.


Bidirectional DOM support

Both JAXB 1.0 and 2.0 define an optional method named getNode( ) for the Marshaller interface. The purpose of this method is to return a DOM Node object that provides access to the object tree through the DOM interfaces. Unlike the marshal( ) method, getNode( ) does not create a copy of your objects. Instead, the DOM Node interface is a façade on top of your object tree. Changes made to the DOM tree are made directly to your object tree, where if you modify the result of a call to marshal( ), you would need to unmarshall the changed DOM Node back into an object tree.

If a JAXB implementation chooses to not provide a full implementation of this method, it will throw a java.lang.UnsupportedOperationException. Unfortunately, neither reference implementation supports this method.

Marshaller properties

As seen in Figure, the Marshaller interface includes getProperty( ) and setProperty( ) methods for setting various options on the output. The JAXB specification defines several properties that all implementations are required to support. Implementations are free to support additional properties. The standard properties for JAXB 1.0 and 2.0 are:


jaxb.encoding

Specifies the output encoding as a java.lang.String. Defaults to UTF-8.


jaxb.formatted.output

A java.lang.Boolean value that, if true, adds linebreaks and indentation to the XML output. Defaults to false.


jaxb.schemaLocation

This property, a java.lang.String, instructs the marshaller to include an xsi:schemaLocation attribute to the XML output. If not specified, no attribute is included.


jaxb.noNamespaceSchemaLocation

This property, a java.lang.String, instructs the marshaller to include an xsi:noNamespaceSchemaLocation attribute to the XML output. If not specified, no attribute is included.

In addition, JAXB 2.0 added a new property:


jaxb.fragment

This property, a java.lang.Boolean, tells the marshaller to treat the result of the marshalling as a fragment, not a full XML document. The default value is false. The effect of this property depends on where the marshaller output is destined.


org.xml.sax.ContentHandler

The Marshaller will not call the startDocument( ) event on the ContentHandler object.


org.w3c.dom.Node

No impact.


java.io.OutputStream or java.io.Writer

XML declaration is not generated.


javax.xml.stream.XMLEventWriter and javax.xml.stream.XMLStreamWriter

START_DOCUMENT and END_DOCUMENT events will not be generated.

The impact on the marshal( ) method that accepts a javax.xml.transform.Result depends on the type of Result object passed and will generally follow one of the rules above.

Unmarshalling

Unmarshalling an XML document to a tree of Java objects is fairly similar to marshalling to an XML document.

If we had this document in a file called james.xml:

<?xml version="1.0" encoding="UTF-8"?>
<person>
    <firstName>James</firstName>
    <lastName>Gosling</lastName>
</person>

Then this code will output Mr. Gosling's first name:

package javaxml3;

import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import com.example.person.Person;

public class PersonUnmarshaller {
    
    public static void main(String[] args) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance("com.example.person");
        Unmarshaller unmarshaller = context.createUnmarshaller(  );
        Person person = (Person) unmarshaller.unmarshal(new File("james.xml"));
        System.out.println(person.getFirstName(  ));

    }

}

Like the Marshaller interface's marshal( ) method, there are a handful of different input options for the unmarshal( ) method, in addition to the java.io.File used in the example above:

  • org.xml.sax.InputSource

  • java.io.InputStream

  • org.w3c.dom.Node

  • javax.xml.transform.Source

  • java.net.URL

JAXB 2.0 adds these additional input options:

  • java.io.Reader

  • javax.xml.stream.XMLStreamReader

  • javax.xml.stream.XMLEventReader

With the Node, Source, XMLStreamReader, and XMLEventReader inputs, there are also overloaded versions that accept both the input mechanism and a Class. These methods return an instance of javax.xml.bind.JAXBElement parameterized with the Class passed to the method. These methods allow you to specify into which type the input XML will be unmarshalled, regardless of the mapping between the root element of the input XML and a JAXB type. For example, let's say we wanted to tolerate misspellings of the root element like:

<?xml version="1.0" encoding="UTF-8"?>
<perso>
    <firstName>James</firstName>
    <lastName>Gosling</lastName>
</perso>

This could be accomplished by using JAXB 2.0 with one of the parameterized methods:

package javaxml3;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;

import com.example.person.Person;

public class MisspelledPersonUnmarshaller {

    public static void main(String[] args) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance("com.example.person");
        Unmarshaller unmarshaller = context.createUnmarshaller(  );

        JAXBElement<Person> element = unmarshaller.unmarshal(new StreamSource(
                "james_misspelled.xml"), Person.class);
        Person person = element.getValue(  );
        System.out.println(person.getFirstName(  ));

    }
}

Unmarshall-time validation

The Unmarshaller interface defines methods setValidating( ) and isValidating( ) to turn on and off JAXB validation of input documents. By default, Unmarshaller instances do not validate. These methods have been deprecated in JAXB 2.0, something we'll discuss later in the "Validation" section.

Unmarshaller properties

Like Marshaller, Unmarshaller defines getProperty( ) and setProperty( ) methods for setting options. However, neither JAXB specifications define any standard properties for Unmarshaller instances.

Validation

JAXB 1.0 contained a Validator interface in the javax.xml.bind package that performed on-demand validation of a tree of JAXB objects. Instances of this interface used the metadata stored within the generated classes to perform the validation. In JAXB 2.0, the Validator interface has been deprecated and implementations are not required to implement it. The JAXB 2.0 reference implementation, for one, throws a java.lang.UnsupportedOperationException if you attempt to create a Validator object.

Instead of a JAXB-specific validation mechanism, JAXB 2.0 is dependent upon the JAXP validation mechanism discussed in Chapter 7. As part of this, on-demand validation was replaced with marshall-time validation. This seems like a bigger change than it actually is, as you can easily simulate on-demand validation by performing a marshall with validation enabled and just not use the result of the marshalling.

To use JAXP validation within either a marshall or an unmarshall operation, create an instance of javax.xml.validation.Schema and pass it to the setSchema( ) method of either an Unmarshaller or Marshaller instance. In Figure, we create an incomplete person object (there's no last name specified). Without validation enabled, the marshaller's output would not be valid according to the schema. But because we enable schema validation, an exception is thrown.

Schema validation when marshalling

package javaxml3;

import java.io.File;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import com.example.person.Person;

public class MarshalValidation {

    public static void main(String[] args) throws Exception {
        // create an incomplete Person object
        Person p = new Person(  );
        p.setFirstName("Burt");

        // create our marshaller
        JAXBContext context = JAXBContext.newInstance(Person.class);
        Marshaller marshaller = context.createMarshaller(  );

        // create a schema
        SchemaFactory sf = SchemaFactory
                .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema schema = sf.newSchema(new File("person.xsd"));

        // assign the schema to the marshaller
        marshaller.setSchema(schema);

        // this will throw a javax.xml.bind.MarshalException
        marshaller.marshal(p, System.out);
    }

}

This same logic can be applied to unmarshalling: create a SchemaFactory, then a Schema, and finally pass the Schema object to the setSchema( ) method of your Unmarshaller instance.

Validation events

By default, any validation error will result in an immediate failure of the marshall or unmarshall operation with an appropriate exception (either MarshalException or UnmarshalException) thrown. For finer control over this behavior, the JAXB API includes a series of interfaces that allow you to intercept validation errors as they occur. These interfaces are shown in Figure. These interfaces are the same in both JAXB 1.0 and JAXB 2.0.

Validation event interfaces


To use these interfaces, create an implementation of the ValidationEventHandler interface and pass it to the setEventHandler( ) method of a Marshaller or Unmarshaller instance. As validation problems occur while marshalling or unmarshalling, your handler's handleEvent( ) method is called with a ValidationEvent object. This object informs you of the severity of the problem, a related exception (if available), and a human-readable message. The event object also contains an implementation of the ValidationEventLocator interface, which allows you to pinpoint where in your object tree or input XML the problem has occurred. The ValidationEvent interface defines three levels of severity: warning, error, and fatal error. There are constants within the ValdiationEvent interface that allow you to write code such as:

if (event.getServerity(  ) == ValidationEvent.WARNING) {
    System.out.println("Warning received. Ignoring it.");
} else {
    System.out.println("Error or Fatal Error received. Uh Oh.");
}

The handleEvent( ) method returns a boolean value that determines whether or not the marshall or unmarshall operation in the process will proceed. If the method returns false or the method throws an unchecked exception, the current operation is halted immediately and an exception is thrown. If the method returns true, the operation proceeds.

If the marshall or unmarshall validation succeeds, your handler's handleEvent( ) method will never be called.


Schema Compilation Customization

JAXB 1.0 and 2.0 provide extensive capabilities for customizing the behavior of the schema compiler through a binding declaration. These declarations can be in within your schema file, an external binding file, or both. Regardless of what file they're located in, the customizations take more or less the same form: elements within the namespace http://java.sun.com/xml/ns/jaxb. One way to think about these elements is as the mirror images of the annotations added in JAXB 2.0. For example, the XmlSchema annotation previously discussed allows you to specify the namespace URI for classes within a package. With the binding declaration package, you can do the opposite: specify the package name for the target namespace of a schema. In fact, thinking about the binding declarations as a kind of annotation makes a lot of sense, since to include them in an XML Schema file, you place the declaration elements within the xs:annotation element.

Inline declarations

To use inline declarations, you must first add the JAXB namespace to your schema document along with a version property indicating which version of JAXB the declarations are targeted. Then, for each schema component you want to add a declaration to, add an xs:annotation element containing an xs:appinfo element. Inside the xs:appinfo element, add your declarations. Figure contains a simple schema annotated with JAXB binding declarations.

A simple annotated schema

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0">
    <xs:annotation>
        <xs:appinfo>
            <jaxb:schemaBindings>
                <jaxb:package name="com.example.artist"/>
            </jaxb:schemaBindings>
        </xs:appinfo>
    </xs:annotation>
    <xs:element name="artist">
        <xs:annotation>
            <xs:appinfo>
                <jaxb:class name="MyArtist"/>
            </xs:appinfo>
        </xs:annotation>
        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string"/>
                <xs:element name="nameForSorting" type="xs:string" minOccurs="0">
                    <xs:annotation>
                        <xs:appinfo>
                            <jaxb:property name="keyName"/>
                        </xs:appinfo>
                    </xs:annotation>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

The reason for needing both xs:annotation and xs:appinfo is that xs:annotation can also contain the xs:documentation element, which allows you to provide documentation for each element within a schema. Do yourself a favor and use xs:documentation, not comments, to document your XML Schema files.


External declaration

By convention, an external binding declaration file is given the extension xjb. This isn't strictly required unless you want to pass in a directory name to the schema compiler and have all of the files within that directory used as external declarations files as mentioned in Figure. An external binding declaration file is an XML document with the root element bindings (in the http://java.sun.com/xml/ns/jaxb namespace). A binding declaration file can contain declarations for one or more schema documents. If the file only applies to a single schema, then the root bindings element will have a schemaLocation attribute whose value is the URI of the schema document as in Figure. If the file applies to multiple schemas, the root bindings element will contain multiple child bindings elements, each with their own schemaLocation element, as seen in Figure.

Binding declaration file applied to a single schema

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings version="2.0" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" schemaLocation="schema.xsd">
    <!-- declaractions go here -->
</jaxb:binding>

Binding declaration file applied to a multiple schemas

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings version="2.0" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <jaxb:bindings schemaLocation="schema1.xsd">
        <!-- declaractions go here -->
    </jaxb:bindings>

    <jaxb:bindings schemaLocation="schema2.xsd">
        <!-- declaractions go here -->
    </jaxb:bindings>

</jaxb:bindings>

Within bindings elements with a schemaLocation attribute, there are one or more child bindings elements with a node attribute. This attribute contains an XPath expression used to find the schema component the contained declarations are applied to. Since bindings elements can be nested, the XPath expressions can be relative expressions based on the current node. Putting these pieces together allows us to extract the binding declarations from Figure into an external file:

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings version="2.0" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <jaxb:bindings schemaLocation="artist.xsd">
        <jaxb:bindings node="/xs:schema">
            <jaxb:schemaBindings>
                <jaxb:package name="com.example.artist"/>
            </jaxb:schemaBindings>
            <jaxb:bindings node="./xs:element[@name='artist']">
                <jaxb:class name="MyArtist"/>
                <jaxb:bindings node=".//xs:element[@name='nameForSorting']">
                    <jaxb:property name="keyName"/>
                </jaxb:bindings>
            </jaxb:bindings>
        </jaxb:bindings>
    </jaxb:bindings>
</jaxb:bindings>

Binding declarations

JAXB 1.0 defined seven binding declarations for use inside jaxb:bindings or xs:appinfo elements. JAXB 2.0 added three additional declarations:


globalBindings

Since JAXB 1.0. Sets various options that affect the schema compilation as a whole such as which Java type is used for collections and whether or not to generate methods to determine if a property has been set explicitly. This declaration can only exist at the schema level. It can contain javaType declarations, described below.


schemaBindings

Since JAXB 1.0. Sets the package name for the schema as well as specifying prefixes and suffixes for generated classes. For example, you can specify that all the names of classes generated from xs:elements will start with My and end with Element. This declaration can only exist at the schema level. The package child element, which defines the package name, see above, can contain a javadoc declaration, described below.


class

Since JAXB 1.0. This declaration sets the name of the generated class for the associated schema component. It can contain a javadoc declaration.


property

Since JAXB 1.0. This declaration customizes the generation of a Java property including the name, as we saw above. It can contain a javadoc declaration, which if provided, is used to generate the Javadoc for both the getter and setter method for the generated property.


javaType

Since JAXB 1.0. This declaration specifies a mapping between an XML type and a Java class or primitive. This declaration can exist inside globalBindings or schemaBindings declarations, in which case the XML type must be specified and the mapping will apply globally or schema-wide, respectively. It can also annotate a schema type component directly, in which case the XML type is not specified. The declaration also allows you to specify methods for reading and writing to a String. If the mapped Java type is a class, not a primitive, the read method is optional if the class defines a constructor with a single String argument.


typesafeEnum

Since JAXB 1.0. This declaration customizes how the schema compiler generates typesafe enumeration classes.


javadoc

Since JAXB 1.0. This declaration allows you to insert custom text into the generated class's Javadoc. It must be specified inside one of the declarations defined above.


dom

Since JAXB 2.0. When this declaration is included, the annotated schema component will be represented in the generated Java classes as an object in a document object model library rather than JAXB types. This is very useful when working with mixed content for which JAXB (and data binding in general) is ill-suited. Although support is only for W3C DOM, implementations are free to support other libraries.


inlineBinaryData

Since JAXB 2.0. This declaration provides control of the binary data optimizations in JAXB 2.0.


factoryMethod

Since JAXB 2.0. This declaration defines the name of the factory method for a type in the generated ObjectFactory class.

For details on these declarations, see the JAXB specifications. You can also download the XML Schemas for the binding declarations from http://java.sun.com/xml/ns/jaxb/bindingschema_1_0.xsd and http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd.

JAXB Callbacks

With JAXB 2.0, callback mechanisms were added to Marshaller and Unmarshaller. The callback mechanism allows objects to be notified throughout the marshalling or unmarshalling process. This mechanism has two facets to it: class-defined and external listener.

Class-defined callbacks

A class bound with JAXB can implement methods to be notified before or after it is marshalled or unmarshalled. These methods are:

private void beforeUnmarshal(Unmarshaller unmarshaller, Object parent)
private void afterUnmarshal(Unmarshaller unmarshaller, Object parent)
private void beforeMarhshal(Marshaller marshaller)
private void afterMarhsal(Marshaller marshaller)

In the case of the before methods, the object is free to modify itself or perform other additional processing.

Note that these methods are not described in an interface. They're discovered by the JAXB implementation by reflection.


External listener

Both Marshaller and Unmarshaller interfaces include an abstract inner class named Listener. By extending this class and passing it to the setListener( ) method of the appropriate class, an external object can be notified before and after each object is marshalled or unmarshalled. Unmarshaller.Listener defines methods named beforeUnmarshal( ) and afterUnmarshal( ):

public static abstract class Listener {
    public void beforeUnmarshal(Object target, Object parent) { }
    public void afterUnmarshal(Object target, Object parent) { }
}

And Marshaller.Listener defines methods named beforeMarshal( ) and afterMarshal( ):

public static abstract class Listener {
    public void beforeMarshal(Object source) { }
    public void afterMarshal(Object source) { }
}

These are abstract classes, not interfaces, specifically so that you only need to override one method. You can, of course, override both. But you don't need to as you would if Listener was an interface.




 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows