Tuesday Dec 02, 2008
Tuesday Dec 02, 2008
Java Architecture for XML Binding (JAXB) provides a fast and convenient way to bind between XML schemas and Java representations, making it easy for Java developers to incorporate XML data and processing functions in Java applications. As part of this process, JAXB provides methods for unmarshalling XML instance documents into Java content trees that represent the structure and content of XML source documents, and then marshalling Java content trees back into XML instance documents. JAXB also provides a way to generate XML schema from Java objects. You can learn more about JAXB in Chapter 17: Binding between XML Schema and Java Classes in the Java EE5 Tutorial.
In this tip, you'll learn several ways of customizing or extending the default behavior of JAXB.
A samples package accompanies the tip. The package includes two files, JAXBSample.java
and schema.xsd that demonstrate some of the techniques covered in this tip. The tip also points to resources that
more fully cover the topic.
The tip is based on JAXB 2.0 or later. You can download a nightly build of JAXB from the JAXB Download page. This tip shows the use of JAXB from a command line. However, JAXB is also available with the NetBeans IDE.
Customizing Namespace Prefixes During Marshalling
Marshalling is the act of creating an XML document from a JAXB-derived Java object tree. When you marshall an XML document
using JAXB 1.0, a Marshaller object, a JAXB object that controls the process of marshalling, provides namespace
declarations in the resulting XML document. The Marshaller does this only when necessary, that is, only when the
declared namespace prefixes are used in a program. Sometimes the Marshaller produces a lot of namespace declarations
that look redundant, for example:
JAXB 2.0 changes this behavior. If you use JAXB 2.0 (or later) to marshal an XML document, the
Marshaller declares all statically known namespace Uniform Resource Identifiers (URIs), that is, those
URIs that are used as element or attribute names in JAXB annotations. JAXB may also declare additional namespaces in the
middle of an XML document, for example when a qualified name (QName) that is used as an attribute or element value
requires a new namespace URI, or when a Document Object Model (DOM) node in a content tree requires a new namespace URI.
This behavior might produce an XML document that has a lot of namespace declarations with automatically-generated
namespace prefixes. The problem is that automatically-generated namespace prefixes such as ns1, ns2, and ns3,
are not user friendly -- they typically do not help people understand the marshalled XML.
Fortunately, JAXB 2.0 (or later) provides a service provider interface (SPI) named
com.sun.xml.bind.marshaller.NamespacePrefixMapper that you can use to specify more
helpful namespace prefixes for marshalling. You implement the SPI and pass it to the Marshaller.
The main methods in the NamespacePrefixMapper class are shown in Table 1.
Table 1: NamespacePrefixMapper Methods |
|||
| Method | Description | ||
|---|---|---|---|
public abstract String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix); |
Returns a list of namespace URIs that should be declared at the root element. | ||
public String[] getPreDeclaredNamespaceUris(); |
Returns a list of namespace URIs or namespace prefix-namespace URI pairs that should be declared at the root element. | ||
public String[] getPreDeclaredNamespaceUris2(); |
Returns a list of namespace prefix-namespace URI pairs that represent namespace bindings available on ancestor elements (that need not be repeated by JAXB.) | ||
public String[] getContextualNamespaceDecls(); |
Returns a namespace prefix-namespace URI pair that should be declared at the root element. | ||
The program in the JAXBSample.java file illustrates the use of these methods. The program marshalls the same
XML document three times, first without the use of a NamespacePrefixMapper class and then with two different
NamespacePrefixMapper implementations.
Follow these steps to build and run the program:
schema.xsd file:
$JAXB_HOME is the directory where you installed JAXB.
JAXBSample class:
Here's what you should see as output:
JAXBSample program marshalls the XML document the first time, it does it without using
a NamespacePrefixMapper class. As a result, the Marshaller automatically
generates a namespace prefix, in this case, ns2.
The second marshalling done by the JAXBSample program uses a NamespacePrefixMapper class
as follows:
The getPreferredPrefix() method in the PreferredMapper class returns
the preferred prefix, in this case, mappedNamespacea to be declared at the root element of the marshalled
XML.
The third marshalling done by the JAXBSample program uses a NamespacePrefixMapper class
as follows:
The getPreDeclaredNamespaceUris() method in the DeclareOnTopMapper class
returns a list of namespace URIs, in this case, a and b, that should be declared at the root element.
The fourth marshalling done by the JAXBSample program uses a NamespacePrefixMapper class
as follows:
The getContextualNamespaceDecls() method in the PreDeclaredMapper class
returns a namespace prefix and namespace URI, in this case, a and mappedNamespacea, that should be declared
at the root element.
There are other ways to customize the way namespaces are handled in JAXB. In particular there are various ways to remove unwanted namespace declarations in marshalled XML. You'll find most of these in the Metro and JAXB forum. Additional techniques are documented on web sites such as Digital Karate and java.net - jaxb users on Nabble.
Also expect enhancements to NamespacePrefixMapper, such as allowing namespace rejection.
If you have a specific use case with or without a solution, or a suggestion for improvement to JAXB, please post it to the
Metro and JAXB forum. You can
also submit suggestions for enhancements to JAXB through the
Issue Tracker.
Customizing Schema Translation
The xjc schema compiler, also called the binding compiler, is an important part of a JAXB implementation. The compiler binds a source XML schema to a set of schema-derived program elements. The binding is described by an XML-based binding language in a binding file. The binding compiler produces a set of packages containing Java source files and JAXB property files.
In most cases, the default bindings generated by the binding compiler are sufficient. However, there are cases where you might want to modify the default bindings. For example, you might want to provide more meaningful package names than are generated by default. You can modify the default bindings by using custom binding declarations which are passed to the JAXB binding compiler. You can make these binding declarations as inline annotations in a source XML schema or declare them in an external binding customizations file.
In addition, you can define binding customizations in different scopes (although not all binding customizations can be defined in each scope). The scopes are:
<globalBindings> element.
<schemaBindings> element.
See Customizing JAXB Bindings in the Java EE5 Tutorial for a fuller description of these scopes.
Binding customizations are defined in the JAXB specification -- you can rely on these no matter which JAXB implementation your
application runs with. However, specific JAXB implementations may provide additional customizations. For example, the JAXB
Reference Implementation (RI) provides additional customizations that are not defined by the JAXB specification. You can learn about
these in Java Architecture for XML Binding
Vendor Customizations. Note that to use vendor-specific extensions you need to launch the xjc binding compiler with
the -extension option.
Customizing Through XJC Plugins
Using an xjc plugin is yet another way of customizing the default behavior of the binding compiler. You can use an existing plugin,
such as the
XJC Fluent API Plugin, which enables the binding
compiler to chain methods, or the XJC Camelcase
Always Plugin, which enables the binding compiler to generate names in camel case. You can find a list of xjc plugins for
JAXB 2.0 on the JAXB2 Commons page. There are some plugins, such
as the episode plugin, that are part of the JAXB RI. You can find these plugins in the jaxb-ri/xjc/src/com/sun/tools/xjc/addon/
directory in jaxb sources. Note that you first need to join the
jaxb sources project, this gives you CVS access to the JAXB RI 2.0 source code.
To use any of these plugins, specify the classpath for the plugin when you launch the xjc compiler:
If none of the existing xjc plugins suits your needs, consider writing your own. It's more difficult than using an existing plugin, but it can give you more flexibility. However, beware of vendor lockin. For instructions on how to write an xjc plugin see the blog Writing a plug-in for the JAXB RI is really easy. In short, here's what you need to do:
com.sun.tools.xjc.Plugin class.META-INF/services/com.sun.tools.xjc.Plugin directory.Further Reading
For more information on customizing JAXB see the following resources:
In addition, the sample applications in the samples directory of the
JAXB
binary download can be helpful,
as well as the Using JAXB
chapter in The Java Web Services Tutorial, and
Chapter 17: Binding between XML Schema and Java Classes
in the Java EE5 Tutorial.
About the Author
Martin Grebac is a staff engineer at Sun Microsystems and the JAXB implementation lead. He is involved in tool-related activities such as Metro plugins for the NetBeans IDE or Eclipse IDE, and runtime integration such as the integration of the Grails framework in GlassFish v2. Read his blog.
Is it possible to generate the xml without any namespaces?
Mapper: not set.
<?xml version="1.0" ncoding="UTF-8" standalone="yes"?>
<JustAnElement xmlns="a">
<foo>true</foo>
</JustAnElement>
I have a client that is passing this xml to us, which is a valid format. However, jaxb doesn't seem to be able to honor this.
Posted by michael on December 19, 2008 at 03:17 PM PST #
Hi,
currently it's not possible to do in JAXB. There are several workarounds that can be applied, you'll find those on google, like e.g. here: http://www.digitalkarate.net/?p=63 , but we're thinking of how to improve JAXB to become more flexible wrt namespace handling for future releases.
Posted by Martin Grebac on January 06, 2009 at 07:37 AM PST #
Hi,
Does anybody know whether JAXB supports the 'list' datatype? I parsed a WSDL file using 'wsimport' to generate List Java datatype OK but JAXB then throwed exception at runtime complaining about missing implementor for the list data type.
Wayne Le-Trung
Posted by Wayne Le-Trung on May 28, 2009 at 05:46 PM PDT #