The JAXB specifications defines three basic packages, shown in Table 13.2. Let us look at the javax.xml.bind package in detail.
javax.xml.bind |
Defines the interfaces and classes to be used by developers. Deals with marshalling, unmarshalling, and validation. |
javax.xml.bind.util |
Contains useful utility classes for use by developers. |
javax.xml.bind.helper |
Not intended to be used by developers. The package provides default implementations of some interfaces in the javax.xml.bind package, for providers to use. |
The entry point for any developer code into JAXB will be a JAXBContext object using the newInstance() method:
JAXBContext context = JAXBContext.newInstance( "com.flutebank.schema" );
The JAXB context initializes the underlying JAXB runtime with the appropriate factory information. The file named jaxb.properties is searched for in the classpath and the class indicated by the javax.xml.bind.context.factory property and is used to create the context. The context is passed the package name, which contains the binding information (multiple package names can be specified using the colon separator, ":"; this allows JAXB to manage the multiple schemas at one time).
The packages specified must contain binding information generated by a single vendor (who provides the JAXB implementation). With multiple packages, the same context can be used to marshall and unmarshall multiple XML documents from different schemas in a single invocation. The JAXB implementation will ensure that each package on the context path has a jaxb.properties file containing a value for the javax.xml.bind.context.factory property and that all these values resolve to the same provider-in short, that the context is specific to a JAXB implementation.
As mentioned earlier, the binding compiler generates the interfaces corresponding to schema constructs as well as the implementation for such interfaces. This raises the question of how multiple implementations can be used in a single application-for example, where an application component must be packaged and distributed and may potentially be used with other application components using another vendor's JAXB implementation. To facilitate such situations, different JAXB contexts would need to be created for different packages and possibly with different class loaders, using the alternate newInstance() method:
JAXBContext context = JAXBContext.newInstance( "com.flutebank.schema", myclassloader );
The javax.xml.bind.Unmarshaller is responsible for unmarshalling XML documents (which are based on the schema) into Java object representations. An Unmarshaller is created from a JAXBContext, using the createUnmarshaller() method:
JAXBContext context = JAXBContext.newInstance( "com.flutebank.schema" ); // create an Unmarshaller Unmarshaller unmars = context.createUnmarshaller();
Once created, the Unmarshaller can be applied to many different XML sources using convenient methods, as Table 13.3 shows.
Unmarshalling method |
Unmarshall from |
---|---|
public void Object umarshall(java.io.File f) |
XML data from a file |
public void Object unmarshall (org.xml.sax.InputSource source) |
From the specified SAX InputSource (See Chapter 9) |
public void Object unmarshall (java.io.InputStream is) |
From any InputStream |
public Object unmarshall (org.w3c.dom.Node node) |
From a W3C DOM tree (See Chapter 9) |
public void Object unmarshall (javax.xml.transform.Source source) |
From the XML Source object. (See Chapter 9) |
public void Object unmarshall(java.net.URL url) |
From a network URL |
The Unmarshaller has two other important functions:
Performing validation during unmarshalling, to verify that the XML conforms to the schema. Upon invoking the setValidating(true), the Marshaller instance will be marked as a validating unmarshaller, and the JAXB provider performs XML validation against the schema used to generate the binding information:
Registering application-defined event handlers that are notified of validation events. Developers write the event handlers by implementing the javax.xml .bind.ValidationEventHandler method and registering this handler with the Unmarshaller, using the setEventHandler method:
JAXBContext context = JAXBContext.newInstance( "com.flutebank.schema" ); // create an Unmarshaller Unmarshaller unmars = context.createUnmarshaller(); MyValidator validr=new MyValidator(): unmars.setEventHandler(validr);
The default validation handler in JAXB will terminate processing upon encountering the first fatal error. So if your application must recover gracefully or conditionally process the XML, consider overriding the default handler. This is analogous to the SAX handlers in Chapter 9.
The javax.xml.bind.Marshaller can be used to marshall object representations into XML format. The XML is based on the schema passed to the binding compiler and used to generate the initial Java bindings.
The marshaller is also created from the context:
JAXBContext context = JAXBContext.newInstance( "com.flutebank.schema" ); // Other code here Marshaller mars = context.createMarshaller(); mars.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE ); mars.setProperty (Marshaller.JAXB_SCHEMA_LOCATION, "http://www.flutebank.com/schema purchaseorder.xsd"); mars.marshall( po, new FileOutputStream("modifiedpurchaseorder.xml"));
Much like the Unmarshaller, the Marshaller can marshal the Java object and its referenced objects (the content tree) into different destinations-files, DOM trees, and so on-summarized in Table 13.4.
Marshalling method |
Marshall the object and referenced objects therein into |
---|---|
public void marshall(Object obj, org.xml.sax.ContentHandler handler) |
SAX2 events |
public void marshall(Object obj, org.w3c.dom.Node node) |
A DOM tree |
public void marshall(Object obj, java.io.OutputStream os) |
An output stream |
public void marshall(Object obj, javax.xml.transform.Result result) |
A javax.xml.transform.Result |
Public void marshall(Object obj, java.io.Writer writer) |
A Writer stream. |
JAXB does not require that the Java content tree be valid or validated against the schema during the marshalling process to give the JAXB implementation the ability to support use cases where partial XML is generated. The provider may choose to disallow marshalling of invalid content and throw a MarshallException.
As with the Unmarshaller, event handlers can be registered with the Marshaller using the setEventHandler(ValidationEventHandler handler) method. Even though there is no way to programmatically enable validation during the marshall operation, it is possible for validation events to be received by the handler. The default handler will stop the marshalling when the first fatal error is encountered.
The Marshaller can be configured using four standard properties, described in Table 13.5, that affect the XML formatting and output. The properties are passed as name-value pairs using the setProperty method, as shown below.
Property |
Description |
---|---|
jaxb.encoding |
The Marshaller will use "UTF-8" by default. If needed the encoding can be changed using this property. |
jaxb.formatted.output |
By default the Marshaller will not format the output with line breaks and indentations. If this property is set to true the XML generated will be formatted for human readability. |
jaxb.schemaLocation |
Used to set the xsi:schemaLocation attribute in the generated XML. (See Chapter 9 for details on this property.) |
jaxb.noNamespaceSchemaLocation |
Used to set the xsi:noNamespaceSchemaLocation property in the generated XML. (See Chapter 9 for details on this property.) |
Marshaller mars = context.createMarshaller(); Mars.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE ); mars.setProperty (Marshaller.JAXB_SCHEMA_LOCATION, "http://www.flutebank.com/schema purchaseorder.xsd");
JAXB defines the javax.xml.bind.Element interface used by the runtime as a marker for identifying the JAXB-generated binding interfaces. It has no methods. In some situations, application developers would need to write code implementing this interface. JAXB users always work with the generated Java binding interface and its methods, which encapsulate the name and value of the corresponding XML element. For example, the billingaddress element is mapped to the Billingaddress interface, which has methods to get/set the relevant types, which themselves are generated bindings. This was shown in Listing 13.2.
In XML schemas, the xsd:any element can be used to refer to elements not specified in the schema. For example, in the following fragment, the occupation element can be extended to include other content with the any element:
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault= "qualified"> <xsd:element name="occupation"> <xsd:complexType> <xsd:sequence> <xsd:element name="Engineer" type="xsd:string"/> <xsd:element name="SeniorEngineer" type="xsd:string"/> <xsd:element name="TechnicalWriter" type="xsd:string"/> <xsd:any minOccurs="0"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema>
This schema results in the generated binding shown below, which must cater to the potential of arbitary XML elements being specified whose name or value is not known at compile time.
public interface OccupationType { String getEngineer(); void setEngineer(String value); javax.xml.bind.Element getAny(); void setAny(javax.xml.bind.Element value); String getTechnicalWriter(); void setTechnicalWriter(String value); String getSeniorEngineer(); void setSeniorEngineer(String value); }
When using this interface in application code, the code is responsible for passing an instance that implements the javax.bind.xml.Element interface, as opposed to the procedure with other Java data types.