Get tips on using enterprise Java technologies and APIs, such as those in Java Platform, Enterprise Edition (Java EE).
Enterprise Tech Tips
Archives
« July 2009
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
31
 
       
Today
Click me to subscribe
Search

Links
 

Today's Page Hits: 1335

« Tech Tips Quiz | Main | Enterprise Tech Tips... »
Tuesday Dec 02, 2008
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:

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:

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.

Posted at 10:39AM Dec 02, 2008 by edort in Metro  |  Comments[3]

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

Post a Comment:
  • HTML Syntax: NOT allowed