How to make jaxb plugin use OffsetDateTime

3.3k views Asked by At

We have a xsd with xs:dateTime fields. This is our in-house internal API, and we can guarantee that the offset data is always included, so that it's ISO-8601 compatible. For example:

2016-01-01T00:00:00.000+01:00

Currently, the jaxb2 plugin maps xs:dateTime to a field of type XMLGregorianCalendar. How to configure the plugin, so that it uses OffsetDateTime instead?

I don't care whether the solution is for maven-jaxb2-plugin, jaxb2-maven-plugin or cxf-codegen-plugin, we'll use whichever works.

2

There are 2 answers

2
AudioBubble On BEST ANSWER

You can use the jaxb2-maven-plugin with a jaxb-bindings file.

First I created a odt.xsd file:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="teste" type="Teste" />
  <xsd:complexType name="Teste">
    <xsd:sequence>
      <xsd:element name="date" type="xsd:dateTime" minOccurs="1"
        maxOccurs="1" nillable="false"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

Then I created a jaxb-bindings.xjb file, that defines the type of the date field, and also the class that converts from and to it:

<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xsd="http://www.w3.org/2001/XMLSchema" jaxb:version="2.0">
    <jaxb:bindings schemaLocation="odt.xsd">
        <jaxb:bindings node="//xsd:element[@name='date']">
            <jaxb:javaType name="java.time.OffsetDateTime"
             parseMethod="xsd.test.OffsetDateTimeAdapter.parse"
             printMethod="xsd.test.OffsetDateTimeAdapter.print" />
        </jaxb:bindings>
        <jaxb:schemaBindings>
            <jaxb:package name="xsd.test" />
        </jaxb:schemaBindings>
    </jaxb:bindings>
</jaxb:bindings>

This file references the xsd.test.OffsetDateTimeAdapter class and the respective methods to convert the OffsetDateTime from and to a String, so I also created it:

package xsd.test;

import java.time.OffsetDateTime;

public class OffsetDateTimeAdapter {

    public static OffsetDateTime parse(String value) {
        return OffsetDateTime.parse(value);
    }

    public static String print(OffsetDateTime value) {
        return value.toString();
    }
}

Then, in pom.xml I've added the configuration for the plugin:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxb2-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>xjc</id>
            <goals>
                <goal>xjc</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <!-- The package of your generated sources -->
        <packageName>xsd.test</packageName>
        <sources>
            <source>src/main/resources/odt.xsd</source>
        </sources>
        <xjbSources>
            <xjbSource>src/main/resources/jaxb-bindings.xjb</xjbSource>
        </xjbSources>
    </configuration>
</plugin>

With this, I've just built the project with mvn clean package and the jar created contains the generated files in the xsd.test package. The Teste class contains the date field as a OffsetDateTime:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Teste", propOrder = {
    "date"
})
public class Teste {

    @XmlElement(required = true, type = String.class)
    @XmlJavaTypeAdapter(Adapter1 .class)
    @XmlSchemaType(name = "dateTime")
    protected OffsetDateTime date;
    // getter and setter
}

With this, the date field is mapped to a OffsetDateTime, using the autogenerated Adapter1 (which internally uses the xsd.test.OffsetDateTimeAdapter class created above). Example of parsing the date from a xml:

ObjectFactory f = new ObjectFactory();
JAXBContext context = JAXBContext.newInstance("xsd.test");
Unmarshaller unmarshaller = context.createUnmarshaller();
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><ns2:teste xmlns:ns2=\"xsd.test\"><date>2016-01-01T00:00+01:00</date></ns2:teste>";
JAXBElement<Teste> jaxElement = unmarshaller.unmarshal(new StreamSource(new ByteArrayInputStream(xml.getBytes())), Teste.class);
OffsetDateTime odt = jaxElement.getValue().getDate();
System.out.println(odt); // 2016-01-01T00:00+01:00

And when marshalling the date to a xml, the OffsetDateTime is directly converted to a String such as 2016-01-01T00:00+01:00.


Another way is to use the command line xjc, which comes with the JDK:

xjc src/main/resources/odt.xsd -d src/main/java/ -p xsd.test -b src/main/resources/jaxb-bindings.xjb

This generates the classes in src/main/java directory, at the xsd.test package.

0
lbruun On

jTextTime library solves this.

The library revolves around the JDK8 OffsetXXX date/time classes as these are the (only) natural equivalent for the XML Schema types date, dateTime and time. It also deals with differences between XML types and JSR-310 types as there's unfortunately not a one-to-one match.

Use like this:

Add dependency:

<dependency>
    <groupId>com.addicticks.oss</groupId>
    <artifactId>jtexttime</artifactId>
    <version> ... latest ...</version>
</dependency>

Create your XJC binding:

Create a file named src/main/xjb/jaxb-datetime-bindings.xjb with content as below. If you are using the JAXB2 Maven Plugin then it will automatically be picked up by the plugin. If you are using another Maven Plugin then you'll have to tell it where to find this file.

<?xml version="1.0" encoding="UTF-8"?>
    <!-- This file is automatically picked up by the jaxb2-maven-plugin
         if it lives in src/main/xjb                                -->
<jxb:bindings   
        xmlns:jxb="http://java.sun.com/xml/ns/jaxb" 
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
        xmlns:xsi="http://www.w3.org/2001/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">

    <jxb:globalBindings>
        <!-- Avoid having to work with XMLGregorianCalendar. 
             Instead, map as follows:

                 XML dateTime   :   OffsetDateTime  
                 XML date       :   OffsetDateTime  (time value truncated)
                 XML time       :   OffsetTime                             -->

        <xjc:javaType adapter="com.addicticks.texttime.jaxb.OffsetDateTimeXmlAdapter"
                      name="java.time.OffsetDateTime" xmlType="xs:dateTime"/>
        <xjc:javaType adapter="com.addicticks.texttime.jaxb.OffsetDateXmlAdapter"
                      name="java.time.OffsetDateTime" xmlType="xs:date"/>
        <xjc:javaType adapter="com.addicticks.texttime.jaxb.OffsetTimeXmlAdapter"
                      name="java.time.OffsetTime" xmlType="xs:time"/>

    </jxb:globalBindings>

</jxb:bindings>

The JAXB2 Maven Plugin will by default pickup all .xjb files in src/main/xjb/ folder, so you don't have to specify the above file in the plugin's config.

The project's README has more information on how to use the library.