Monday 1 February 2016

JodaTime, XSD, XJC

For Java 1.7 and earlier JodaTime (http://www.joda.org/joda-time/) is a great replacement for the standard XML / XSD date time object that Java creates when using XJC.

From Java 1.8 onwards the date time support in Java is much better and is basically a JodaTime implementation (JSR310).

Specific Bindings

To use JodaTime create a bindings file which contains an XPath location to the correct element, such as,


    <jxb:bindings namespace="http://www.me.com/my/message" schemaLocation="my-message.xsd">
        <jxb:schemaBindings>
            <jxb:package name="com.me.my.message.generated" />
        </jxb:schemaBindings>
        
        <jxb:bindings node="//xs:complexType[@name='Message']/xs:sequence/xs:element[@name='PublishDateTime']">
            <xjc:javaType name="org.joda.time.DateTime" adapter="com.me.my.util.JodaDateTimeAdaptor" />    
        </jxb:bindings>
    </jxb:bindings>

Adaptor

The JodaDateTimeAdaptor will do the actual conversion and can be reused.

public class JodaDateTimeAdaptor
    extends XmlAdapter<String, DateTime>
{
    /**
     * Convert a DateTime to String.
     *
     * @param datetime DateTime to be converted.
     * @return The value of datetime formatted as per ISO8601, or the empty string if date is null.
     */
    @Override
    public String marshal(final DateTime datetime)
    {
        return datetime.toString();
    }

    /**
     * Convert a String representation of a date into a Date object.
     *
     * @param str String representation of a date/time, in the ISO8601. May be null or empty.
     * @return str converted to a Date object. If str is null or empty, null is returned.
     */
    @Override
    public DateTime unmarshal(final String str)
    {
        if (str == null || str.length() == 0)
        {
            return null;
        }
        return DateTime.parse(str);
    }
}

Global Bindings

Global bindings can also be used but these don't work when one XSD imports another and you want to use an adaptor on both.  You end up with a class name clash because the automatically generated Adaptors appear in a 

    org.w3._2001.xmlschema.Adaptor1.java
    org.w3._2001.xmlschema.Adaptor2.java
    ...

However, for a single xsd or single project this may work.  To use the global bindings instead of the specific ones use


    <!-- generateName allows the typed enums -->
    <jxb:globalBindings typesafeEnumMemberName="generateName" >
        <jxb:serializable uid="1" />
        <!-- use JODA-Time DateTime for xs:date -->
        <jxb:javaType name="org.joda.time.DateTime" xmlType="xs:date"
            parseMethod="com.me.my.util.JodaDateAdaptor.unmarshal"
            printMethod="com.me.my.util.JodaDateAdaptor.marshal"/>
        <jxb:javaType name="org.joda.time.DateTime" xmlType="xs:dateTime"
            parseMethod="com.me.my.util.JodaDateTimeAdaptor.unmarshal"
            printMethod="com.me.my.util.JodaDateTimeAdaptor.marshal"/>
    </jxb:globalBindings>


No comments:

Post a Comment