97

When JAXB marshals a date object (XMLGregorianCalendar) into an xsd:dateTime element. How can you specify the format of the resulting XML?

For example: The default data format uses milliseconds <StartDate>2012-08-21T13:21:58.000Z</StartDate> I need to omit the milliseconds. <StartDate>2012-08-21T13:21:58Z</StartDate>

How can I specify the output form/date format that I want it to use? I'm using javax.xml.datatype.DatatypeFactory to create the XMLGregorianCalendar object.

XMLGregorianCalendar xmlCal = datatypeFactory.newXMLGregorianCalendar(cal);
Marome
  • 47
  • 1
  • 11
Young Fu
  • 1,257
  • 1
  • 9
  • 12

5 Answers5

139

You can use an XmlAdapter to customize how a date type is written to XML.

package com.example;

import java.text.SimpleDateFormat;
import java.util.Date;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class DateAdapter extends XmlAdapter<String, Date> {

    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public String marshal(Date v) throws Exception {
        synchronized (dateFormat) {
            return dateFormat.format(v);
        }
    }

    @Override
    public Date unmarshal(String v) throws Exception {
        synchronized (dateFormat) {
            return dateFormat.parse(v);
        }
    }

}

Then you use the @XmlJavaTypeAdapter annotation to specify that the XmlAdapter should be used for a specific field/property.

@XmlElement(name = "timestamp", required = true) 
@XmlJavaTypeAdapter(DateAdapter.class)
protected Date timestamp; 

Using a xjb binding file:

<xjc:javaType name="java.util.Date" xmlType="xs:dateTime"
        adapter="com.example.DateAdapter"/>

will produce the above mentioned annotation.
(By eventually adding the xjc namespace: xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc")

Sven Döring
  • 3,927
  • 2
  • 14
  • 17
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • 2
    Thanks for this answer! Is it possible to add the annotation via the xsd or a binding file? I only found your frequently quoted blog entry about bindings.xml, but this covers other aspects, I think. – guerda Jul 16 '13 at 09:37
  • 11
    As @PeterRader mentioned, SimpleDateFormat is not thread-safe - if two threads were to enter either marshal or unmarshal simultaneously, you could get very unpredictable results. This would be very difficult to reproduce in normal testing, but under load could happen, and would be exceedingly difficult to diagnose. It's better to create a new SimpleDateFormat with marshal and unmarshal (but use a static format string if necessary). – Colselaw Feb 28 '15 at 13:32
  • 1
    I did this and it almost worked. However I was getting `Class has two properties of the same name "timeSeries"` error - this was solved by putting the annotation at the getter and not at the member level. (Thanks to @megathor from http://stackoverflow.com/questions/6768544/jaxb-class-has-two-properties-of-the-same-name) – gordon613 Jun 30 '15 at 15:47
  • 1
    @gordon613 - This article will provide some additional information on where to put the annotation: http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html – bdoughan Jun 30 '15 at 15:49
  • 3
    Since the critical block is protected with "synchronized" there is not any problem. There will be (performance) problem if multiple calls are made. – Mike Argyriou Aug 14 '17 at 13:29
  • 1
    This solution does not work when the classes are generated from XSD. – Namphibian Sep 18 '18 at 02:34
  • There is something wrong/missing with the XJB file part. There is no `parseDateTime` and `formatDateTime` provided in the DateAdapter above. I tried to use `marshal` and `unmarshal` instead, but I end up with the error `non-static method marshal(java.util.Date) cannot be referenced from a static context` when I compile my code. Do you know how to fix this? – Carrm Nov 02 '18 at 15:00
  • how to add milliseconds optional? – Roberto Jan 16 '19 at 14:18
  • The xjb binding does not produce the mentioned annotation. – Albert Hendriks May 14 '20 at 10:28
21

I use a SimpleDateFormat to create the XMLGregorianCalendar, such as in this example:

public static XMLGregorianCalendar getXmlDate(Date date) throws DatatypeConfigurationException {
    return DatatypeFactory.newInstance().newXMLGregorianCalendar(new SimpleDateFormat("yyyy-MM-dd").format(date));
}

public static XMLGregorianCalendar getXmlDateTime(Date date) throws DatatypeConfigurationException {
    return DatatypeFactory.newInstance().newXMLGregorianCalendar(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(date));
}

The first method creates an instance of XMLGregorianCalendar that is formatted by the XML marshaller as a valid xsd:date, the second method results in a valid xsd:dateTime.

Andrea Luciano
  • 461
  • 3
  • 5
2

Very easy way to me. Formatting XMLGregorianCalendar for marshalling in java.

I just create my data in the good format. The toString will be called producing the good result.

public static final XMLGregorianCalendar getDate(Date d) {
    try {
        return DatatypeFactory.newInstance().newXMLGregorianCalendar(new SimpleDateFormat("yyyy-MM-dd").format(d));
    } catch (DatatypeConfigurationException e) {
        return null;
    }
}
Iván
  • 129
  • 1
  • 7
0

https://www.baeldung.com/jaxb

public class DateAdapter extends XmlAdapter<String, Date> {

    private static final ThreadLocal<DateFormat> dateFormat 
      = new ThreadLocal<DateFormat>() {

        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    }

    @Override
    public Date unmarshal(String v) throws Exception {
        return dateFormat.get().parse(v);
    }

    @Override
    public String marshal(Date v) throws Exception {
        return dateFormat.get().format(v);
    }
}
Mike
  • 20,010
  • 25
  • 97
  • 140
0

Usage:

import com.company.LocalDateAdapter.yyyyMMdd;

//...

@XmlElement(name = "PROC-DATE")
@XmlJavaTypeAdapter(yyyyMMdd.class)
private LocalDate processingDate;

LocalDateAdapter

import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class LocalDateAdapter extends XmlAdapter<String, LocalDate> {

  public static final class yyyyMMdd extends LocalDateAdapter {
    public yyyyMMdd() {
      super("yyyyMMdd");
    }
  }

  public static final class yyyy_MM_dd extends LocalDateAdapter {
    public yyyy_MM_dd() {
      super("yyyy-MM-dd");
    }
  }

  private final DateTimeFormatter formatter;

  public LocalDateAdapter(String pattern) {
    formatter = DateTimeFormat.forPattern(pattern);
  }

  @Override
  public String marshal(LocalDate date) throws Exception {
    return formatter.print(date);
  }

  @Override
  public LocalDate unmarshal(String date) throws Exception {
    return formatter.parseLocalDate(date);
  }
}
erran
  • 1,300
  • 2
  • 15
  • 36
Mike
  • 20,010
  • 25
  • 97
  • 140
  • Question not about LocalDate but XMLGregorianCalendar – krund Jul 14 '22 at 10:49
  • @krund question is about 'How can you specify the format of the resulting XML?'. You have an example how to do it, replace the date code import for the one you need. – Mike Jul 14 '22 at 10:54
  • OP mentioned XMLGregorianCalendar, so this https://stackoverflow.com/a/23376876/6860091 response solved problem for me. – krund Jul 14 '22 at 13:50
  • @krund good for you – Mike Jul 15 '22 at 09:27