In this post I will give an example of generation of Java class model from an XSD using custom adapter for a date element. By default, the date fields of the generated class model are of XMLGregorianCalendar type. However, you do not want to write a custom wrappers to convert java.util.Date to XMLGregorianCalendar, but want directly operate with java.util.Date object when dealing with the model classes.
One way to do it is to use @XmlJavaTypeAdapter annotation on your models date fields. But, you do not want to manually modify generated model classes since, in your situation, you model is regenerated frequently. Thus, you need a way to tell to your JAXB binding compiler (xjc) how exactly it should act upon date fields.
At the bottom of this post you will find a link to the working project. You may use it as you wish and adapt to your problem.
This example is composed of the following component:
- java 5
- maven 2
- jaxb 2.0
- maven-jaxb2-plugin 0.8.1
Maven project
To generate class model from XSD I will use Maven plug-in. All you need is to specify the following details in you POM:
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
- Java version, which should be at least 5
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<encoding>UTF-8</encoding>
<configuration>
</plugin>
<pluginRepositories>
<pluginRepository>
<id>maven2-repository.dev.java.net</id>
<url>http://download.java.net/maven/2</url>
<layout>default</layout>
</pluginRepository>
<pluginRepository>
<id>maven-repository.dev.java.net</id>
<url>http://download.java.net/maven/1</url>
<layout>legacy</layout>
</pluginRepository>
</pluginRepositories>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
</plugin>
Schema
I will use the following simple schema for the example:
<s:schema xmlns:s="http://www.w3.org/2001/XMLSchema">
<s:element name="getValue">
<s:complexType>
<s:sequence>
<s:element name="date" type="s:dateTime"/>
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
It has all I need for my example; namely, simple date element.
If you generate the model at this stage you will get the following model class:
@XmlRootElement(name = "getValue")
public class GetValue {
@XmlElement(required = true)
@XmlSchemaType(name = "time")
protected XMLGregorianCalendar date;
...
}
As you can see, the date property is of type XMLGregorianCalendar and as you remember, our goal is to have it of type java.util.Date.
Place your schema file under src/main/resources directory. This is the default location the maven-jaxb2-plugin is looking for schemas. You can configure another location by using schemaDirectory element on a plugin.
Create Adapter
The first thing we need to do is to create an adapter class that will be responsible for the conversion of date values from/to XMLGregorianCalendar and java.util.Date. In order to plugin our custom implementation of the adapter we will need to comply with the contracts defined by JAXB. Namely, we'll have to implement javax.xml.bind.annotation.adapters.XmlAdapter interface.
The XmlAdapter JavaDoc specifies the following:
Some Java types do not map naturally to a XML representation, for example HashMap or other non JavaBean classes. Conversely, a XML repsentation may map to a Java type but an application may choose to accesss the XML representation using another Java type... In both cases, there is a mismatch between bound type , used by an application to access XML content and the value type, that is mapped to an XML representation.
This abstract class defines methods for adapting a bound type to a value type or vice versa...
In other words, we need to implmement two methods:
- XmlAdapter.marshal
- XmlAdapter.unmarshal
Below is of the the possible implementations:
public class DateAdapter extends XmlAdapter<String, Date> {
public Date unmarshal(String value) {
try {
return new SimpleDateFormat("yyyy-MM-dd").parse(value);
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
}
public String marshal(Date value) {
return new SimpleDateFormat("yyyy-MM-dd").format(value);
}
}
Create JAXB binding
In order to tell JAXB binding compiler how it should deal with the date types I will customize JAXB date binding by means of custom binding declaration made in external file passed to the binding compiler. I will create a binding file - call it date-binding.xsd.
Note: There are two types of custom binding declarations: inine and external file. In case of inline declarion the custom binding declation is made inside a schema, e.g.
<schema ...>
<complexType name="size">
<annotation>
<appinfo>
<jaxb:class name="widgetSize" />
</appinfo>
</annotation>
</complexType>
...
<schema>
Since, in my example I am using the external file (date-binding.xsd) to declare JAXB binding the declaration will look as follows:
<jaxb:bindings
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jaxb:extensionBindingPrefixes="xjc"
jaxb:version="2.0">
<jaxb:globalBindings >
<xjc:javaType name="java.util.Date"
xmlType="xsd:dateTime"
adapter="org.devtoolbox.adapter.DateAdapter"/>
</jaxb:globalBindings>
</jaxb:bindings>
Place the binding file under the same directory you have placed your schem, namely src/main/resources.
Few things to note:
- to declare date binding I am using <xjc:javaType> customization and not <jaxb:javaType> customization. <xjc:javaType> allows you to specify an
XmlAdapter-derived class, instead of parse&print method pair.
- I am using <jaxb:globalBindings > to apply binding to all dateTime elements defined withing a schema. Alternative would be to use the following construct:
<jxb:bindings schemaLocation = "xs:anyURI">
<jxb:bindings node = "xs:string">*
[your binding declaration]
<jxb:bindings>
</jxb:bindings>
where schemaLocation is a URI reference to the remote schema and node is an XPath 1.0 expression that identifies the schema node within schemaLocation to which the given binding declaration is associated.
Example Maven Project
As promised, below is the example. You can either
When you get the project run "mvn compile" command from a command line (make sure you have maven installed). The generated model classes will be located under target directory.
Recent Comments