/ BINDING, BINDINGS, CUSTOM, CUSTOMIZATION, CUSTOMIZE, JAXB, MAVEN PLUGIN, PARENT CLASS, SERIALIZABLE, XJC, XML

Customize your JAXB bindings

JAXB is a bridge between the Java and the XML worlds, enabling your code to transparently marshalls and unmarshalls your Java objects to and from XML. In order to do this, you should have a class representing your XML-Schema. This class is created by the xjc. In most cases, xjc creates a class that won’t suit your needs. In this article, we’ll see why it does so and what we can do to customize this behaviour.

Prerequisite

This article suppose you use a Java Development Kit 6.0. Be wary that different updates of this same JDK 6 use different JAXB versions:

Update JAXB version

3

2.0.3

4

2.1.3

Concurrent technologies

Before diving into JAXB, one should ask oneself whether using JAXB is necessary. There’s a plethora of frameworks whose goal is to serialize/deserialize Java objects to and from XML:

  • Historically speaking (and from my humble knowledge), Castor XML was the first widely used framework to manage Java XML serialization.
  • Since Java 1.4, two classes XMLEncoder and XMLDecoder are available. Their are respectively equivalent to ObjectOutputStream and ObjectInputStream, only they produce XML instead of bytes.
  • Finally, XStream is a 3rd party framework that is fast to run and easy to use.

All these solutions are very good in reading/producing XML, yet they completely ignore the binding part. Binding is the creation of the Java class from the schema. Of course, JAXB has this feature.

XJC

xjc is the executable used to create Java classes from XML schemas. Available syntax are DTD, XML-Schema, RELAX NG, RELAX NG Compact and WSDL. Since I now exclusively use Maven to build my projects, I won’t use xjc directly but I’ll configure the Maven POM to use it. The first thing to do is to add the java.net repository to your POM (or your settings file, but I prefer the former):

<repositories>
    <repository>
        <id>maven2-repository.dev.java.net</id>
        <name>Java.net Maven 2 Repository</name>
        <url>http://download.java.net/maven/2</url>
    </repository>
</repositories>
<pluginRepositories>
    <pluginRepository>
        <id>maven2-repository.dev.java.net</id>
        <name>Java.net Maven 2 Repository</name>
        <url>http://download.java.net/maven/2</url>
    </pluginRepository>
</pluginRepositories>

Now, you’re ready to use the plugin:

<build>
    <plugins>
        <plugin>
            <groupId>org.jvnet.jaxb2.maven2</groupId>
            <artifactId>maven-jaxb2-plugin</artifactId>
            <version>0.7.1</version>
            <configuration>...</configuration>
        </plugin>
    </plugins>
</build>

Use the plugin with the following command line: mvn org.jvnet.jaxb2.maven2:maven-jaxb2-plugin:generate.

Common configuration

The previous command line will fail since we didn’t specify any configuration yet. The following should be assumed a good default:

<configuration>
    <schemaDirectory>src/main/resources/schema</schemaDirectory>
    <generateDirectory>src/main/generated</generateDirectory>
    <removeOldOutput>false</removeOldOutput>
</configuration>

This tells xjc to look for all xsd files under the src/main/schema directory and generate the classes under src/main/generated. It will use the binding files (*.xjb) under the former directory.

I sincerely advise you to set the removeOldOutput to false since xjc will erase all the directory content. If you use a source code management tool, this will include the directories used by your SCM (.cvs or .svn), a very bad idea.

You need two things still. The first is to set the the compiler level to at least 1.5 in order to annotations to work:

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.6</source>
        <target>1.6</target>
    </configuration>
</plugin>

Maven only accepts a single source directory. The second thing to do is to make the  src/main/generated directory to Maven when building the project. It can be done with the help of the builder plugin:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>add-source</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>add-source</goal>
            </goals>
            <configuration>
                <sources>
                    <source>src/main/generated</source>
                </sources>
            </configuration>
        </execution>
    </executions>
</plugin>

Now using the previous command line will produce something useful! Try it and you will have some nice surprise.

Customization

In order to customize your bindings, you basically have 2 options:

  • polluting your XML Schema with foreign namespaces,
  • or keeping your XML Schema pure and putting all your bindings into a external file.

From the terms I used, you can guess I prefer option 2. This is the right time to use a binding file. Create a file named binding.xjb under src/main/resources/schema.

Package name

Of  course, the package name is not something desirable: by default, it is the XML-Schema namespace minus http:// plus _package. The first customization is to change this behaviour. Put the following content into your binding file:

<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
                xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
                xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
    version="2.1">
    <schemaBindings>
        <package name="ch.frankel.blog.jaxb" />
    </schemaBindings>
</bindings>

If you have the following error, you should use update 14 from JDK 6:

`java.lang.LinkageError: JAXB 2.0 API is being loaded from the bootstrap classloader, but this RI (from jar:file:/C:/${user.home}/.m2/repository/com/sun/xml/bind/jaxb-impl/2.1.10/jaxb-impl-2.1.10.jar!/com/sun/xml/bind/v2/model/impl/ModelBuilder.class) needs 2.1 API. Use the endorsed directory mechanism to place jaxb-api.jar in the bootstrap classloader.

— http://java.sun.com/j2se/1.5.0/docs/guide/standards/

If not, chances are your classes will have been generated under the suitable package name.

Serializable

Managing your entities will probably require you to have them serializable. In order to achieve this, you’ll have to modify your binding file to add the java.io.Serializable interface to your classes along with a serialVersionUID. Every class generated will now be serializable and have the specified uid: a limitation of this process is that each of your generated class will have the same uid.

<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
                xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
                xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
                xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
    version="2.1">
    <schemaBindings>
        <package name="ch.frankel.blog.jaxb" />
    </schemaBindings>
    <globalBindings>
        <serializable uid="100" />
     </globalBindings>
</bindings>

Superclass

It is sometimes beneficial that all your bound classes inherit from a common class, one that is not described in the XSD. This may happen if this superclass is entirely for technical purpose (sucha as strong typing) and shouldn’t clutter the schema.

In order to do so, two actions are necessary:

  • enable the extended mode for the xjc compiler. Just add <extension>true</extension> under your configuration in the POM
  • use the superClass tag in the http://java.sun.com/xml/ns/jaxb/xjc namespace

This class won’t be generated by xjc so you are free to create it as you please (making it abstract, adding methods and so on).

<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
                xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
                xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
                xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
    version="2.1">
    <schemaBindings>
        <package name="ch.frankel.blog.jaxb" />
    </schemaBindings>
    <globalBindings>
        <serializable uid="100" />
        <xjc:superClass name="ch.frankel.blog.jaxb.XmlSuperClass" />
    </globalBindings>
</bindings>

Data type

By default, xjc will bind the schema to the most precise data type available. For example, a XML-Schema date type will be bound to a Java javax.xml.datatype.XMLGregorianCalendar. This has the drawback of coupling your bound classes to the JAXB API and forcing you to use classes you don’t really need. For purpose of example, check how to convert a java.sql.Date from the database to your entity javax.xml.datatype.XMLGregorianCalendar: have fun!

To ease your development, you can provide both during binding and marshalling/unmarshalling adapters meant to pass to and from XML. This is declared as such in the bindings file:

<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
                xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
                xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd
                                    http://www.w3.org/2001/XMLSchema http://www.w3.org/2001/XMLSchema.xsd"
    version="2.1">
    <bindings schemaLocation="schema.xsd">
        <schemaBindings>
            <package name="ch.frankel.blog.jaxb" />
        </schemaBindings>
        <globalBindings>
            <javaType name="java.util.Date" xmlType="xs:date"
                parseMethod="ch.frankel.blog.jaxb.JaxbConverter.parseDate"
                printMethod="ch.frankel.blog.jaxb.JaxbConverter.printDate" />
            <serializable uid="100" />
            <xjc:superClass name="ch.frankel.blog.jaxb.AbstractSuperClass" />
        </globalBindings>
    </bindings>
</bindings>

Notice you got another namespace to manage. This will generate an adapter class under the org.w3._2001.xmlschema package you’ll have to use during marshalling/unmarshalling process.

Tweaking the output

Not every information you’ll need in Java will be provided by the XML-Schema. What about compairing our objects with equals()?

Not every schema you’ll use will have been designed by you or your team. Some will be legacy, some will be standards…​ That doesn’t mean you should suffer for other’s lack of good practice. What if the schema uses uppercase tags? Surely, you want your generated classes to follow Sun coding conventions.

The Sun JAXB team provides plugins to the XJC compiler that resolve the above problems. Have a look at them.

For our example, we choose to add hashCode(), equals() and toString() methods to our generated class. A plugin exists to do just that. You only have to configure this generation in the POM, adding the wanted arguments to the compiler and adding the plugins needed to manage these arguments:

<configuration>
...
    <args>
        <arg>-XtoString</arg>
        <arg>-Xequals</arg>
        <arg>-XhashCode</arg>
    </args>
    <plugins>
        <plugin>
            <groupId>org.jvnet.jaxb2_commons</groupId>
            <artifactId>jaxb2-basics</artifactId>
            <version>0.5.0</version>
        </plugin>
     </plugins>
</configuration>

In this specific case, you’ll have to add two more dependencies (Commons Lang and JAXB Basics runtime) to your POM since the generated classes will depend on them:

<dependencies>
...
    <dependency>
        <groupId>commons-lang</groupId>
        <artifactId>commons-lang</artifactId>
        <version>2.4</version>
    </dependency>
    <dependency>
        <groupId>org.jvnet.jaxb2_commons</groupId>
        <artifactId>jaxb2-basics-runtime</artifactId>
        <version>0.5.0</version>
    </dependency>
</dependencies>

JAXB Persistence Bindings

To go even further and have a single entity able to be marshalled by JAXB and managed by your persistence layer, you can use the HyperJaxb product.

HyperJaxb2 cares about Hibernate managed persistence whereas HyperJaxb3 focuses on JPA managed persistence. The latter seems to be suffering from a lack of documentation, but the objective looks promising.

Conclusion

When reading from and writing to XML, one may be provided with a XML Schema. In this case, it is a good practice to bind the schema to Java. JAXB is the standard solution to achieve this. Yet, in many cases, the generated Java classes will be lacking some features: this shouldn’t be considered the end of the world, since the binding process can be customized to take many parameters into account.

You’ll find the sources for this article here.

Nicolas Fränkel

Nicolas Fränkel

Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Also double as a trainer and triples as a book author.

Read More
Customize your JAXB bindings
Share this