JAXB

CXF @WebService and XMLGregorianCalendar with JAXB

Lately we’ve done a lot of work with some different web-services and at one point we discovered that some people have trouble formatting their timestamps properly. Good solution would be to ask people to use a proper standard and not re-invent their own wheels, but that’s not always a possibility. Fortunately there’s a rather simple solution for this. All you need is to define a @XmlJavaTypeAdapter and teach it how to convert different values into suitable formats.

JAXB and XML by default uses a timestamp format of “yyyy-MM-dd’T’HH:mm:ss’Z'” which translates to 2015-04-11T14:15:12.000 roughly and some people think it’s ok to send us SQL ISO format timestamps that look very similar – specifically missing the T letter in the timestamp, like this: “yyyy-MM-dd HH:mm:ss”.

For example, in my web service entity I have a RelaxedXMLGregorianCalendarAdapter in use which is our own implementation:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType
@XmlRootElement(name = "Metainfo")
public class Metainfo {
    @XmlJavaTypeAdapter(RelaxedXMLGregorianCalendarAdapter.class)
    protected XMLGregorianCalendar generationTime;
}

And here’s the implementation for the adapter (note that we turn null, empty string and specifically a word “infinity” into null’s):

import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

public class RelaxedXMLGregorianCalendarAdapter extends XmlAdapter<String, XMLGregorianCalendar> {

	@Override
	public String marshal(XMLGregorianCalendar v) {
		return v.toXMLFormat();
	}

	@Override
	public XMLGregorianCalendar unmarshal(String v) throws Exception {

		if (v == null) {
			return null;
		}
		
		if (v.trim().isEmpty()) {
			return null;
		}
		
		// Who thinks that this is OK for a date field?
		if ("infinity".equals(v.trim())) {
			return null;
		}
                // Replace the space with T to match our default pattern.
		return DatatypeFactory.newInstance().newXMLGregorianCalendar(v.replace(" ", "T"));
	}
}

So there you have it. This is how you can customize how JAXB is handling some type conversions and pretty much you can customize anything JAXB does with this approach. Like teach it to treat -1 for specific fields as null or teach it understand different date formats.