Customizing JAXB

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

  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.

Comments:

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 07:17 AM 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 05, 2009 at 11:37 PM 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 10:46 AM PDT #

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

Posted by Stu Panther on September 17, 2009 at 11:42 PM PDT #

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>

Posted by Aldo Mendoza on November 26, 2009 at 12:36 PM PST #

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

Posted by Martin Grebac on December 03, 2009 at 10:34 PM PST #

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.

Posted by David Johnson on December 08, 2009 at 08:18 AM PST #

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.

Posted by Martin Grebac on December 09, 2009 at 05:12 PM PST #

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

Posted by guest on June 09, 2010 at 12:57 AM PDT #

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

Posted by John Doe on September 28, 2010 at 04:14 AM PDT #

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.

Posted by Tom on October 18, 2010 at 06:06 PM PDT #

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?

Posted by Stuart on October 25, 2010 at 04:40 AM PDT #

Post a Comment:
Comments are closed for this entry.
About

edort

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today