Jaxb unmarshal timestamp

Я не могу заставить JAXB отключить отметку времени в серверном приложении Restaase JAX-RS.

Мой класс выглядит следующим образом:

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "foo")
public final class Foo {
    // Other fields omitted

    @XmlElement(name = "timestamp", required = true)
    protected Date timestamp;

    public Foo() {}

    public Date getTimestamp() {
        return timestamp;
    }

    public String getTimestampAsString() {
        return (timestamp != null) ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp) : null;
    }

    public void setTimestamp(final Date timestamp) {
        this.timestamp = timestamp;
    }

    public void setTimestamp(final String timestampAsString) {
        try {
            this.timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(timestampAsString);
        } catch (ParseException ex) {
            this.timestamp = null;
        }
    }
}

Любые идеи?

Спасибо.

Ответ 1

JAXB может обрабатывать класс java.util.Date. Однако он ожидает формат:

"yyyy-MM-dd'T'HH: mm: ss" вместо "yyyy-MM-dd HH: mm: ss"

Если вы хотите использовать этот формат даты, я бы предложил использовать XmlAdapter, он выглядел бы примерно так:

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

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

public class DateAdapter extends XmlAdapter<String, Date> {

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

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

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

}

Затем вы должны указать этот адаптер на свой ресурс timestamp:

import java.util.Date;

import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlAccessorType(XmlAccessType.NONE) 
@XmlRootElement(name = "foo") 
public final class Foo { 
    // Other fields omitted 

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

    public Foo() {} 

    public Date getTimestamp() { 
        return timestamp; 
    } 

    public void setTimestamp(final Date timestamp) { 
        this.timestamp = timestamp; 
    } 

}

Ответ 2

JAXB не может маршалировать объекты Date напрямую, потому что у них недостаточно информации, чтобы быть однозначной. JAXB представил для этой цели класс XmlGregorianCalendar, но очень неприятно использовать его напрямую.

Я предлагаю изменить ваше поле timestamp как XmlGregorianCalendar и изменить различные методы, чтобы обновить это поле, сохраняя при этом общедоступный интерфейс, где это возможно.

Если вы хотите сохранить поле Date, вам нужно будет реализовать собственный класс XmlAdapter, чтобы сообщить JAXB как превратить ваш Date в и из XML.

Ответ 3

Чтобы заставить XML-маршаллер генерировать xsd: дату, отформатированную как YYYY-MM-DD без определения XmlAdapter, я использовал этот метод для создания экземпляра javax.xml.datatype.XMLGregorianCalendar:

public XMLGregorianCalendar buildXmlDate(Date date) throws DatatypeConfigurationException {
    return date==null ? null : DatatypeFactory.newInstance().newXMLGregorianCalendar(new SimpleDateFormat("yyyy-MM-dd").format(date));
}

В результате я инициализировал поле XMLGregorianCalendar класса, сгенерированного компилятором JAXB (в Eclipse):

  Date now = new Date();
  ...
  report.setMYDATE(buildXmlDateTime(now));
  ...
  JAXBContext context = JAXBContext.newInstance(ReportType.class);
  Marshaller m = context.createMarshaller();
  m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
  m.marshal(new ObjectFactory().createREPORT(report), writer);

И получил тег, отформатированный как ожидалось:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<REPORT>
   ...
   <MY_DATE>2014-04-30</MY_DATE>
   ...
</REPORT>

Ответ 4

Использование этого адаптера должно быть безопасным по потоку:

public class DateXmlAdapter extends XmlAdapter<String, Date> {

    /**
     * Thread safe {@link DateFormat}.
     */
    private static final ThreadLocal<DateFormat> DATE_FORMAT_TL = new ThreadLocal<DateFormat>() {

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

    };

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

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

}