X

Customizing JAXB

Guest Author

by Martin Grebac

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:



       <?xml version="1.0"?>
<root>
<ns1:element xmlns:ns1="urn:foo"> ... </ns1:element>
<ns2:element xmlns:ns2="urn:foo"> ... </ns2:element>
<ns3:element xmlns:ns3="urn:foo"> ... </ns3:element>
</root>




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
MethodDescription
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:


  1. Compile the schema.xsd file:

        xjc schema.xsd





    This step generates the binding classes necessary for compilation of the program.
  2. Compile the binding classes generated in the previous step:

         javac -cp $JAXB_HOME/lib/jaxb-impl.jar:. a/\*.java





    where $JAXB_HOME is the directory where you installed JAXB.

  3. Compile and execute the JAXBSample class:

       javac -cp $JAXB_HOME/lib/jaxb-impl.jar:. JAXBSample.java
    java -cp $JAXB_HOME/lib/jaxb-impl.jar:. JAXBSample






Here's what you should see as output:



   Mapper:  not set.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:JustAnElement xmlns:ns2="a">
<foo>true</foo>
</ns2:JustAnElement>
-----------------------
Mapper: PreferredMapper
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a">
<foo>true</foo>
</mappedNamespacea:JustAnElement>
-----------------------
Mapper: DeclareOnTopMapper
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a" xmlns:mappedNamespace
b="b">
<foo>true</foo>
</mappedNamespacea:JustAnElement>
-----------------------
Mapper: PreDeclaredMapper
<xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a">
<foo>true</foo>
</mappedNamespacea:JustAnElement>
-----------------------





When the 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.



   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:JustAnElement xmlns:ns2="a">
<foo>true</foo>
</ns2:JustAnElement>




The second marshalling done by the JAXBSample program uses a NamespacePrefixMapper class
as follows:



   NamespacePrefixMapper m = new PreferredMapper();
marshal(jc, e, m);
public static class PreferredMapper extends NamespacePrefixMapper {
@Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
return "mappedNamespace" + namespaceUri;
}
}




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.



   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a">
<foo>true</foo>
</mappedNamespacea:JustAnElement>




The third marshalling done by the JAXBSample program uses a NamespacePrefixMapper class
as follows:



   m = new DeclareOnTopMapper();
marshal(jc, e, m);
public static class DeclareOnTopMapper extends PreferredMapper {
@Override
public String[] getPreDeclaredNamespaceUris() {
return new String[] {"a", "b"};
}
}




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.



   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a" xmlns:mappedNamespace
b="b">
<foo>true</foo>
</mappedNamespacea:JustAnElement>




The fourth marshalling done by the JAXBSample program uses a NamespacePrefixMapper class
as follows:



   m = new PreDeclaredMapper();
marshal(jc, e, m);
public static class PreDeclaredMapper extends PreferredMapper {
@Override
public String[] getContextualNamespaceDecls() {
return new String[] {"a", "mappedNamespacea"};
}
}




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.



   <xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a">
<foo>true</foo>
</mappedNamespacea:JustAnElement>




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:

  • Global: A binding customization defined in global scope is applicable to all schema elements in which this customization is defined
    and all other schemas that are imported into the schema. You specify a binding declaration in global scope with the
    <globalBindings> element.
  • Schema. A binding customization defined in schema scope is only applicable to the schema in which the customization is defined.
    You specify a binding declaration in schema scope with the <schemaBindings> element.
  • Definition: A customization value in binding declarations of a type definition and global declaration has definition scope.
    A definition scope covers all schema elements that reference the type definition or the global declaration.
  • Component: A customization value in a binding declaration has component scope if the customization value applies only to the
    schema element that was annotated with the binding declaration.

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:



   xjc -cp plugin.jar -help




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:

  • Extend the com.sun.tools.xjc.Plugin class.
  • Put a service file in the META-INF/services/com.sun.tools.xjc.Plugin directory.
  • Package everything in a JAR file.

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.

Join the discussion

Comments ( 12 )
  • michael Friday, December 19, 2008

    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.


  • Martin Grebac Tuesday, January 6, 2009

    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.


  • Wayne Le-Trung Thursday, May 28, 2009

    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


  • Stu Panther Friday, September 18, 2009

    Hi,

    is there anyway to configure JAXB so that namespace can be applied to a node other than the root node. eg the contents of the body elment in a soap message shall conform to schema a and be in namespaceA.

    thanks


  • Aldo Mendoza Thursday, November 26, 2009

    Hi, is it possible to generate an XML with the namespaces but without any prefix like xmnls:ns3="MyURI"

    I want it to be like this:

    <BsqClixNumCliInfoRequest xmlns="http://myuri.com/BusquedaDex/ws/schemas">

    <uid>UID123412341236</uid>

    <paisId>1</paisId>

    </BsqClixNumCliInfoRequest>


  • Martin Grebac Friday, December 4, 2009

    Hi,

    please see my reply above wrt the same example. This is currently not possible directly via JAXB api. See above for alternative approach.

    Martin Grebac


  • David Johnson Tuesday, December 8, 2009

    I have a schema with a single namespace, and have tried to use a NamespacePrefixMapper implementation to force JAXB to use this as the default namespace rather than generating the "ns2" prefix.

    From what I can see, this isn't possible. The "" prefix my code returns from getPreferredPrefix is ignored, because somewhere along the line the JAXBContext defined an empty ("") default namespace of that takes precedence. The XML still contains the ns2 prefix, and it appears that although I can change the prefix, I can't remove it.

    I also tried implementing getPreDeclaredNamespaceUris2() to specify a prefix of "" associated with my namespace. This resulted in the correct default namespace in the root element, but a duplicate namespace declaration for the ns2 prefix is still added.

    Is there any way to remove this unnecessary prefix? Seems like a lot of people are scratching their heads with this problem. I've seen a response from a JAXB dev saying essentially that there is no problem since the XML is still valid, but for me this is a show-stopper due to limitations of the clients receiving my XML. I'm almost to the point of abandoning JAXB for something else.


  • Martin Grebac Thursday, December 10, 2009

    Please make sure you use the latest version of jaxb from jaxb.dev.java.net. If it still doesn't work for you, please file an issue with reproducible testcase and we'll look into it.


  • guest Wednesday, June 9, 2010

    Hi, is it possible to generate an XML with the namespaces but without any prefix like xmnls:ns2="MyURI" ,i dont found any workoround on google!!!

    I want it to be like this:

    <abds xmlns="http://myuri.com/ws/schemas">

    <user>UID123412341236</uid>

    <pass>1</paisId>

    </abds>

    pls help


  • John Doe Tuesday, September 28, 2010

    The link to the work around is broken. Any update on whether this issue is fixed yet?


  • Tom Tuesday, October 19, 2010

    See this post by misdoro for the least ugly (and future proof) solution....

    http://forums.java.net/jive/message.jspa?messageID=476885#476885

    The package-info.java will be created by xjc but if you don't use this then google around.

    Using prefix="" also removes the namespace prefix entirely.


  • Stuart Monday, October 25, 2010

    Hi Martin,

    To remove namespace prefixes you included a link above. Unfortunately this link doesn't seem to work anymore. Can you offer any advice?


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha