Introduction

JAX-B bindings is a great tool when it comes to customizing the generation of Java classes from a set of XML schema using xjc.
The output of a xjc compilation can be customized through:

  • Embedded bindings: where bindings is provided through an external XML file. There is an unofficial convention that says binding files use the *.xjb extension
  • External bindings:: where binding is directly embedded into the XSD using <xs:annotation><xs:appinfo> tags

The JAXB reference documentation provided extensive information regarding the use of JAXB bindings.
This post lists the most common uses cases for JAXB customizations and shows how to handle them using both external and embedded bindings.

Skeletons

External bindings

External bindings are XML files whose root element is the bindings tag. Bindings files are supplied at generation tag to xjc using the -b option as:

xjc -b <binding-file> <xsd-file>

The skeleton of an external binding file is as following :

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings 
	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" 
	xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
	xmlns:xs="http://www.w3.org/2000/10/XMLSchema-instance"
	xs:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
	jaxb:version="2.0">
		:	
		jaxb binding tags here (jaxb:globalBindings, jaxb:bindings)
		:
</jaxb:bindings>

Embedded bindings

Embedded bindings are provided directly in the schema files using the <xs:annotation><xs:appinfo> tags as following:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
	targetNamespace="your-target-namespace" 
	elementFormDefault="qualified" 

	xmlns="your-target-namespace"
	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" 
	xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
	jaxb:version="2.0"
	jaxb:extensionBindingPrefixes="xjc">
	
	<xs:annotation>
		<xs:appinfo>
			:
			jaxb binding tags here (jaxb:globalBindings, jaxb:bindings)
			:
		</xs:appinfo>
	</xs:annotation>
	
	
	:
	Content of the XML schema here
	:
	:
</xs:schema>

Global JAX-B options

Global options are set once and once only for a given xjc compilation through the globalBindings tag. The once only limitation induces that :

  • For external bindings, the globalBindings must appear only once in all xjb files used for a given xjc compilation.
    Because we tend to share global bindings across all xjc compilation in a given project, global bindings are usually defined in a global.xjb
    file which is included in all xjc compilations.
  • For embedded bindings, the globalBindings must appear only once in all xsd files used for a given xjc compilation.
    You will therefore have to choose which xsd file globalBindings appear and possibly make sure this file is included in all xjc compilations.

External bindings

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings 
	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" 
	xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
	xmlns:xs="http://www.w3.org/2000/10/XMLSchema-instance"
	xs:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
	jaxb:version="2.0">

	<jaxb:globalBindings fixedAttributeAsConstantProperty="true" ...other-options.... >
		:
		: other globalBindings like serializable or data handlers
		:
	</jaxb:globalBindings>
</jaxb:bindings>

Embedded bindings

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
	targetNamespace="your-target-namespace" 
	elementFormDefault="qualified" 

	xmlns="your-target-namespace"
	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" 
	xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
	jaxb:version="2.0"
	jaxb:extensionBindingPrefixes="xjc">
		
	<xs:annotation>
		<xs:appinfo>
			<jaxb:globalBindings fixedAttributeAsConstantProperty="true"  ...other-options... >
					:
					: other globalBindings like serializable or data handlers
					:
			</jaxb:globalBindings>
		</xs:appinfo>
	</xs:annotation>
		:
		:
		:
	</xs:schema>

Generating Serializable class

Serializable classes can be generated by adding the <serializable uid="1" /> tag in the globalBindings section. the value of uid specifies the value that will be used when generating the private final static long serialVersionUID attribute in the Java classes.

In both external en embedded bindings, Serializable classes are generated as following :

<jaxb:globalBindings>
	<jaxb:serializable uid="1" />
</jaxb:globalBindings>

Setting a common superclass

Setting a common superclass is a global binding where you can specify that each class generated by xjc should extend a given custom class.
With both external or embedded bindings, setting the superclass is done using a tag provided by the namespace

External bindings

<jaxb:globalBindings>
	<xjc:superClass name="com.example.sandbox.jaxb.JaxBSuperclass" />
</jaxb:globalBindings>

Setting the package

External bindings

The package for the classes generated from a given schema is set through a schema specific bindings. A bug in the latest version of JAX-B prevents from setting a global package for all classes. Instead, you must provide a package customization for each xsd file.

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings 
	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" 
	xmlns:xs="http://www.w3.org/2000/10/XMLSchema-instance"
	xs:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
	jaxb:version="2.0">

	<jaxb:bindings schemaLocation="../xsd/store.xsd">
		<jaxb:schemaBindings>
			<jaxb:package name="com.example.sandbox.ext.xsd.store" />
		</jaxb:schemaBindings>
	</jaxb:bindings>

	<jaxb:bindings schemaLocation="../xsd/book.xsd">
		<jaxb:schemaBindings>
			<jaxb:package name="com.example.sandbox.ext.xsd.book" />
		</jaxb:schemaBindings>
	</jaxb:bindings>
</jaxb:bindings>

Note: this method requires that each xsd file defines its own targetSchema otherwise xjc will raise an error as two bindings will apply to the same schema.

Embedded bindings

Package definition is set for each xsd file as :

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 	 	
	targetNamespace="http://www.example.com/xsd/book" 
	xmlns="http://www.example.com/xsd/book"
	elementFormDefault="qualified" 

	xmlns:xs="http://www.w3.org/2001/XMLSchema"
	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
	jaxb:version="2.0"	
	>
	
	<xs:annotation>
		<xs:appinfo>
    		<jaxb:schemaBindings >
    			<jaxb:package name="com.example.sandbox.internal.xsd.store"/>
    		</jaxb:schemaBindings>    		
		</xs:appinfo>
	</xs:annotation>

		:
		content of the XML schema
		:
</xs:schema>

Customizing generated data types

This is done using the javaType global binding.
Customizing generated data type comes into play when the default Java type associated with given XML type is not satisfactory. The bindings allow to specify an alternative Java type to map the XML type to.

For example, the default Java type associated with a date XML element is a GregorianCalendar. Most would prefer using a java.util.Date java type instead. In this case, the following binding is provided

<jaxb:globalBindings>
	<jaxb:javaType name="java.util.Date"  xmlType="xs:date" hasNsContext="false" 
		parseMethod="com.example.sandbox.jaxb.DateAdapter.parseDate" 
		printMethod="com.example.sandbox.jaxb.DateAdapter.printDate"/>
								
</jaxb:globalBindings>

The code for the DateAdapter class is as follows:

package com.example.sandbox.jaxb;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.xml.bind.DatatypeConverter;

public class DateAdapter {

	public static Date parseDate(String s) {
	    return DatatypeConverter.parseDate(s).getTime();
	  }
	
	  public static String printDate(Date dt) {
	    Calendar cal = new GregorianCalendar();
	    cal.setTime(dt);
	    return DatatypeConverter.printDate(cal);
	  }
}

Customize the binding for a specific complex type / class

The jaxb:bindings allows to target a specific element in the schema and apply customization to this specific element. For instance to customize the name of the generated class.

External bindings

<jaxb:bindings schemaLocation="../xsd/book.xsd" >
	<jaxb:bindings node="//xs:complexType[@name='Book']">
		<jaxb:class name="MyBook" />
	</jaxb:bindings>
	
	<jaxb:bindings node="//xs:complexType[@name='Book']//xs:element[@name='author']">
		<jaxb:property name="authors"/>
	</jaxb:bindings>	
		
</jaxb:bindings>

Embedded bindings

<xs:annotation>
	<xs:appinfo>
		<jaxb:bindings node="//xs:complexType[@name='Book']">
			<jaxb:class name="MyBook" />
		</jaxb:bindings>
			
		<jaxb:bindings node="//xs:complexType[@name='Book']//xs:element[@name='author']">
			<jaxb:property name="authors"/>
		</jaxb:bindings>	
	</xs:appinfo>
</xs:annotation>

Customize enumeration bindings from simple types

XSD definition

<xs:simpleType name="BookCategory">
	<xs:restriction base="xs:string">
		<xs:enumeration value="FICTION" />
		<xs:enumeration value="HISTORY" />
		<xs:enumeration value="HUMOUR" />
		<xs:enumeration value="MISTERY" />
		<xs:enumeration value="THRILLER" />
	</xs:restriction>
</xs:simpleType>

External bindings

<jaxb:bindings schemaLocation="../xsd/book.xsd" >
	<jaxb:bindings node="//xs:simpleType[@name='BookCategory']">
		<jaxb:typesafeEnumClass name="BookCategoryEnum">
			<jaxb:typesafeEnumMember value="FICTION" name = "MY_FICTION"  />
		</jaxb:typesafeEnumClass>
	</jaxb:bindings>				
</jaxb:bindings>

Embedded bindings

<xs:annotation>
	<xs:appinfo>
		<jaxb:bindings node="//xs:simpleType[@name='BookCategory']">
			<jaxb:typesafeEnumClass name="BookCategoryEnum">
				<jaxb:typesafeEnumMember value="FICTION" name = "MY_FICTION"  />
			</jaxb:typesafeEnumClass>
		</jaxb:bindings>				
	</xs:appinfo>
</xs:annotation>

Unbound generation result

@XmlType(name = "BookCategory", namespace = "http://www.example.com/xsd/book")
@XmlEnum
public enum BookCategory {

    FICTION,
    HISTORY,
    HUMOR,
    MISTERY,
    THRILLER;

    public String value() {
        return name();
    }

    public static BookCategory fromValue(String v) {
        return valueOf(v);
    }

}

Bound generated result

@XmlType(name = "BookCategory", namespace = "http://www.example.com/xsd/book")
@XmlEnum
public enum BookCategoryEnum {

    @XmlEnumValue("FICTION")
    MY_FICTION("FICTION"),
    HISTORY("HISTORY"),
    HUMOR("HUMOR"),
    MISTERY("MISTERY"),
    THRILLER("THRILLER");
    private final String value;

    BookCategoryEnum(String v) {
        value = v;
    }

    public String value() {
        return value;
    }

    public static BookCategoryEnum fromValue(String v) {
        for (BookCategoryEnum c: BookCategoryEnum.values()) {
            if (c.value.equals(v)) {
                return c;
            }
        }
        throw new IllegalArgumentException(v);
    }

}

References

Advertisements