SCDJWS Study Guide: JAXB


Printer-friendly version Printer-friendly version | Send this 
article to a friend Mail this to a friend


Previous Next vertical dots separating previous/next from contents/index/pdf Contents

JAXB Binding Framework

The JAXB binding framework is implemented in three Java packages:

  • The javax.xml.bind package defines abstract classes and interfaces that are used directly with content classes.

The javax.xml.bind package defines the Unmarshaller, Validator, and Marshaller classes, which are auxiliary objects for providing their respective operations.

The JAXBContext class is the entry point for a Java application into the JAXB framework. A JAXBContext instance manages the binding relationship between XML element names to Java content interfaces for a JAXB implementation to be used by the unmarshal, marshal and validation operations.

The javax.xml.bind package also defines a rich hierarchy of validation event and exception classes for use when marshalling or unmarshalling errors occur, when constraints are violated, and when other types of errors are detected.

  • The javax.xml.bind.util package contains utility classes that may be used by client applications to manage marshalling, unmarshalling, and validation events.
  • The javax.xml.bind.helper package provides partial default implementations for some of the javax.xml.bind interfaces. Implementations of JAXB can extend these classes and implement the abstract methods. These APIs are not intended to be directly used by applications using JAXB architecture.

The main package in the JAXB binding framework, javax.bind.xml, is described in more detail below.


JAXB binding framework package – javax.xml.bind


The JAXB API, defined in the javax.xml.bind package, is a set of interfaces through which client applications communicate with code generated from a schema. The main client entry point into the binding framework is the JAXBContext class. It provides an abstraction for managing the XML-Java binding information necessary to implement the JAXB binding framework operations: unmarshal, marshal and validate.

These three aspects of JAXB are covered by three separate interfaces. Instances of those interfaces can be created from a JAXBContext object:

  • Unmarshaller: governs the process of deserializing XML data into Java content trees, optionally validating the XML data as it is unmarshalled;

package javax.xml.bind;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import javax.xml.bind.JAXBException;
import javax.xml.bind.PropertyException;
import javax.xml.bind.UnmarshallerHandler;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.transform.Source;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

public interface Unmarshaller {
     public Object unmarshal(File f) throws JAXBException;
     public Object unmarshal(InputStream is) throws JAXBException;
     public Object unmarshal(URL url) throws JAXBException;
     public Object unmarshal(InputSource source) throws JAXBException;
     public Object unmarshal(Node node) throws JAXBException;
     public Object unmarshal(Source source) throws JAXBException;
     public UnmarshallerHandler getUnmarshallerHandler();
     public void setValidating(boolean validating) throws JAXBException;
     public boolean isValidating() throws JAXBException;
     public void setEventHandler(ValidationEventHandler handler) throws JAXBException;
     public ValidationEventHandler getEventHandler() throws JAXBException;
     public void setProperty(String name, Object value) throws PropertyException;
     public Object getProperty(String name) throws PropertyException;
}

  • Marshaller: governs the process of serializing Java content trees back into XML data;

package javax.xml.bind;
import java.io.OutputStream;
import java.io.Writer;
import javax.xml.bind.JAXBException;
import javax.xml.bind.PropertyException;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.transform.Result;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;
 
public interface Marshaller {
    public static final String JAXB_ENCODING = "jaxb.encoding";
    public static final String JAXB_FORMATTED_OUTPUT = "jaxb.formatted.output";
    public static final String JAXB_SCHEMA_LOCATION = "jaxb.schemaLocation";
    public static final String JAXB_NO_NAMESPACE_SCHEMA_LOCATION = "jaxb.noNamespaceSchemaLocation";
 
    public void marshal(Object obj, Result result) throws JAXBException;
    public void marshal(Object obj, OutputStream os) throws JAXBException;
    public void marshal(Object obj, Writer writer) throws JAXBException;
    public void marshal(Object obj, ContentHandler handler) throws JAXBException;
    public void marshal(Object obj, Node node) throws JAXBException;
           
    public org.w3c.dom.Node getNode(Object contentTree) throws JAXBException;
    public void setProperty(String name, Object value) throws PropertyException;
    public Object getProperty(String name) throws PropertyException;
    public void setEventHandler(ValidationEventHandler handler) throws JAXBException;
    public ValidationEventHandler getEventHandler() throws JAXBException;
}
 

  • Validator: performs the validation on an in-memory object graph.

package javax.xml.bind;
import javax.xml.bind.JAXBException;
import javax.xml.bind.PropertyException;
import javax.xml.bind.ValidationEventHandler;
 
public interface Validator {
    public void setEventHandler(ValidationEventHandler handler) throws JAXBException;
    public ValidationEventHandler getEventHandler() throws JAXBException;
    public boolean validate(Object subrootObj) throws JAXBException;
    public boolean validateRoot(Object rootObj) throws JAXBException;
    public void setProperty(String name, Object value) throws PropertyException;
    public Object getProperty(String name) throws PropertyException;
}

JAXBContext provides an abstraction for managing the XML/Java binding information necessary to implement the unmarshal, marshal and validate operations. The JAXBContext actual implementation is vendor-dependent. A client application obtains new instances of this class by means of the newInstance(contextPath) method; for example:

JAXBContext jc = JAXBContext.newInstance( "com.acme.foo:com.acme.bar" );

The contextPath parameter contains a list of Java package names that contain schema-derived interfaces--specifically the interfaces generated by the JAXB binding compiler.

Each schema is compiled into a single package, which means that you can assemble them at run-time by providing multiple package names. The client application MUST supply a context path which is a list of colon (':') separated java package names that contain schema derived classes. In this way, the unmarshaller will look at a document and figure out which package to use. This makes it easy to read in different types of documents without knowing their type in advance. The value of this parameter initializes the JAXBContext object to enable management of the schema-derived interfaces.

To this end, the JAXB provider implementation MUST supply an implementation class containing a method with the following signature:

public static JAXBContext createContext(String contextPath,
                                        ClassLoader classLoader)
                          throws JAXBException;

Note: The JAXB provider implementation must generate a jaxb.properties file in each package containing schema-derived classes. This property file must contain a property named javax.xml.bind.context.factory whose value is the name of the class that implements the createContext API.

The class supplied by the provider does not have to be assignable to javax.xml.bind.JAXBContext, it simply has to provide a class that implements the createContext API. By allowing for multiple Java packages to be specified, the JAXBContext instance allows for the management of multiple schemas at one time.

Unmarshalling

The Unmarshaller class in the javax.xml.bind package provides the client application that contains classes generated by JAXB binding compiler, the ability to convert XML data into a tree of Java content objects.

The unmarshal method for a schema (within a namespace) allows for any global XML element declared in the schema to be unmarshalled as the root of an instance document. The JAXBContext object allows the merging of global elements across a set of schemas (listed in the contextPath). Since each schema in the schema set can belong to distinct namespaces, the unification of schemas to an unmarshalling context should be namespace-independent. This means that a client application is able to unmarshal XML documents that are instances of any of the schemas listed in the contextPath; For example, please note that the last line will have error.

JAXBContext jaxbContent = JAXBContext.newInstance("com.acme.foo:com.acme.bar" );
Unmarshaller u = jaxbContent.createUnmarshaller();
 
//The following two line codes are OK
FooObject fooObj = (FooObject)u.unmarshal( new File( "foo.xml" ) ); // ok
BarObject barObj = (BarObject)u.unmarshal( new File( "bar.xml" ) ); // ok

// The following line code will generate a error b/c "com.acme.baz" not in contextPath
BazObject bazObj = (BazObject)u.unmarshal( new File( "baz.xml" ) );

There are other overloaded versions that take different types of input, such as InputStream or InputSource. You can even unmarshal a javax.xml.transform.Source object. All in all, it's similar to the way DOM trees are parsed.

In the previous version of JAXB, this functionality was provided as a method on the generated class. Since one cannot add methods to existing classes, this design made it impossible to unmarshal them. By moving it into a separate Unmarshaller interface, the new API makes it possible to support this in a future version.

JAXB also supports unmarshalling via a SAX ContentHandler. You can send SAX events to the unmarshaller and have it unmarshal objects. This enhances the connectivity of the Unmarshaller considerably. For example, you can parse the header of a message by using JAXB and send the body of the message to another component. With ContentHandler support, this can be done efficiently.

By default, Unmarshaller is very forgiving. Even if a document is invalid, it tries to recover from errors. If the document is so broken that it cannot be read, an UnmarshalException will be thrown.

It's often desirable to get more information about errors or reject documents with errors. The first step to do this is to set ValidationEventHandler to the Unmarshaller. A ValidationEventHandler can explicitly tell a JAXB implementation whether it should reject a document or try to recover from errors. It also gives you more information, such as line numbers, about errors.

An Unmarshaller can validate a document with the schema while unmarshalling. With this option turned on, it rejects anything short of a valid document. However, W3C XML Schema validation can be very costly.

Another possibility is to set up a SAX pipeline in such a way that your XML parser does the validation; alternately, you could install a stand-alone validator in the pipeline. In this way, for example, you can change your schema to change what you get from the compiler, while maintaining the scrutiny of the original schema.

Marshalling

A Marshaller is used to write an object graph into XML. To write an object o to a file, you would do

Marshaller marshaller = context.createMarshaller();

marshaller.marshal(o, new FileOutputStream("foo.xml") );

There are other overloaded versions which allow you to produce XML as a a DOM tree or as SAX events. For example, by using StringWriter, you can marshal an object into a string. You can also marshal an object graph to a javax.xml.transform.Result object.

In the previous version of JAXB, this functionality was provided as a method on the generated class, and the number of formats was limited. Making it a separate interface enables mapping of existing classes to XML in the future.

In the previous version of JAXB, there was no provision for controlling the formatting. In the new API, you can control the behavior of marshalling by setting Marshaller properties. For example, you can toggle indentation of the XML. More importantly, the mechanism is extensible, which means JAXB providers can expose advanced features.

Although you can customize the behavior of marshalling to some degree by using this mechanism, there's a good chance it doesn't fill your needs completely. For example, some people might want to use tabs for indentation and others might prefer spaces. In some schemas, certain elements cannot be indented without changing the meaning. You might want to control the namespace prefixes. Or you might want to add a processing instruction to the document. In addition, relying on vendor-specific properties compromises portability.

The new version of JAXB can produce XML as SAX events. That is, you can pass ContentHandler and have it receive SAX events from a JAXB object. This gives client apps plenty of chances to modify XML. For example, you can add and remove elements or attributes, use one of the freely available serializers ( XMLWriter by David Megginson, org.apache.xml.serialize.XMLSerializer) for better output, or write your own XML serializer that prints XML in your preferred way.

Finally, you can ask a Marshaller to marshal an invalid object graph by setting a ValidationEventHandler. If a provider supports error recovery, you can tell it to write XML even if it's incomplete.

Validation

The Validator class in the javax.xml.bind package is responsible for controlling the validation of content trees during runtime. When the unmarshalling process incorporates validation and it successfully completes without any validation errors, both the input document and the resulting content tree are guaranteed to be valid. By contrast, the marshalling process does not actually perform validation. If only validated content trees are marshalled, this guarantees that generated XML documents are always valid with respect to the source schema.

There are three forms of Validation in JAXB:

  1. Unmarshal-Time Validation (All JAXB Providers are REQUIRED to support this operation).

This form of validation enables a client application to receive information about validation errors and warnings detected while unmarshalling XML data into a Java content tree and is completely orthogonal to the other types of validation. To enable or disable it use method Unmarhaller.setValidating(...).

  1. On-Demand Validation (All JAXB Providers are REQUIRED to support this operation).

This form of validation enables a client application to receive information about validation errors and warnings detected in the Java content tree. At any point, client applications can call the Validator.validate(...) method on the Java content tree (or any sub-tree of it).

  1. Fail-Fast Validation (JAXB Providers are NOT REQUIRED support this type of validation).

This form of validation enables a client application to receive immediate feedback about modifications to the Java content tree that violate type constraints on Java Properties as defined in the specification. Of the JAXB Providers that do support this type of validation, some may require you to decide at schema compile time whether or not a client application will be allowed to request fail-fast validation at runtime.

NOTE: The Validator class is responsible for managing On-Demand Validation. The Unmarshaller class is responsible for managing Unmarshal-Time Validation during the unmarshal operations. Although there is no formal method of enabling validation during the marshal operations (there is no setValidating() method in Marshaller class), the Marshaller may detect errors, which will be reported to the ValidationEventHandler registered on it.

JAXB has the capability to validate an object graph in memory without actually writing it to XML. This allows client apps to check if a graph is okay and ready to process; if not, validation will identify objects that contain errors so that, for example, client apps can ask users to fix those.

The following code validates the object "item":

...
JAXBContext jaxbContext = JAXBContext.newInstance(packageName);
ObjectFactory itemMaker = new ObjectFactory();
Item item = itemMaker.createItem();
Validator validator = jaxbContext.createValidator();
 
if(! validator.validate(item)) {
     System.err.println("Not valid !!!");
}             
...

You need to register ValidationEventHandler with the Validator, if you want to receive detailed information about errors.

You can also first marshal an object graph and then validate XML (for example by Java API for validators). But doing so makes it much harder to associate errors with their sources, which makes debugging harder for humans. Validation after marshalling will give you errors like "missing <foo> element," but you can hardly know what is actually wrong in the object graph.

Validity is not enforced while you are modifying an object graph; you ALWAYS have to explicitly validate it. To edit a valid object graph into another valid object graph, you may need to go through invalid intermediate states. If validity is enforced on every step of mutation, this becomes impossible.

If the client application does not set an event handler on its Validator, Unmarshaller, or Marshaller prior to calling the validate, unmarshal, or marshal methods, then a default event handler will receive notification of any errors or warnings encountered. The default event handler will cause the current operation to halt after encountering the first error or fatal error (but will attempt to continue after receiving warnings).

There are three ways to handle events encountered during the unmarshal, validate, and marshal operations:

  • Use the default event handler.

The default event handler will be used if you do not specify one via the setEventHandler APIs on Validator, Unmarshaller, or Marshaller.

  • Implement and register a custom event handler.

Client applications that require sophisticated event processing can implement the ValidationEventHandler interface and register it with the Unmarshaller and/or Validator.

  • Use the ValidationEventCollector utility.

For convenience, a specialized event handler is provided that simply collects any ValidationEvent objects created during the unmarshal, validate, and marshal operations and returns them to the client application as a java.util.Collection.

Validation events are handled differently, depending on how the client application is configured to process them. However, there are certain cases where a JAXB Provider indicates that it is no longer able to reliably detect and report errors. In these cases, the JAXB Provider will set the severity of the ValidationEvent to FATAL_ERROR to indicate that the unmarshal, validate, or marshal operations should be terminated. The default event handler and ValidationEventCollector utility class must terminate processing after being notified of a fatal error. Client applications that supply their own ValidationEventHandler should also terminate processing after being notified of a fatal error. If not, unexpected behavior may occur.


Previous Next vertical dots separating previous/next from contents/index/pdf Contents

  |   |