javajava-timemaven-jaxb2-pluginjaxb2-maven-plugincxf-codegen-plugin

How to make jaxb plugin use OffsetDateTime


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.


Solution

  • 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.