Como você especifica o formato de data usado quando JAXB empacota xsd: dateTime?

86

Quando JAXB empacota um objeto de data ( XMLGregorianCalendar) em um elemento xsd: dateTime. Como você pode especificar o formato do XML resultante?

Por exemplo: O formato de dados padrão usa milissegundos <StartDate>2012-08-21T13:21:58.000Z</StartDate> , preciso omitir os milissegundos. <StartDate>2012-08-21T13:21:58Z</StartDate>

Como posso especificar o formato de saída / data que desejo usar? Estou usando javax.xml.datatype.DatatypeFactorypara criar o XMLGregorianCalendarobjeto.

XMLGregorianCalendar xmlCal = datatypeFactory.newXMLGregorianCalendar(cal);
Young Fu
fonte

Respostas:

126

Você pode usar um XmlAdapterpara personalizar como um tipo de data é gravado em 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);
        }
    }

}

Em seguida, você usa a @XmlJavaTypeAdapteranotação para especificar que XmlAdapterdeve ser usado para um campo / propriedade específico.

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

Usando um arquivo de ligação xjb:

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

irá produzir a anotação mencionada acima.
(Por, eventualmente, adicionar o xjcnamespace: xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc")

bdoughan
fonte
2
Obrigado por esta resposta! É possível adicionar a anotação via xsd ou um arquivo de ligação? Eu só encontrei sua entrada de blog frequentemente citada sobre bindings.xml, mas isso cobre outros aspectos, eu acho.
guerda
9
Como @PeterRader mencionou, SimpleDateFormat não é seguro para threads - se duas threads entrarem em empacotamento ou desempacotamento simultaneamente, você poderá obter resultados muito imprevisíveis. Isso seria muito difícil de reproduzir em testes normais, mas sob carga poderia acontecer e seria extremamente difícil de diagnosticar. É melhor criar um novo SimpleDateFormat com marshal e unmarshal (mas use uma string de formato estático se necessário).
Colselaw
1
Eu fiz isso e quase funcionou. No entanto, eu estava recebendo um Class has two properties of the same name "timeSeries"erro - isso foi resolvido colocando a anotação no getter e não no nível do membro. (Agradecimentos a @megathor de stackoverflow.com/questions/6768544/… )
gordon613
1
@ gordon613 - Este artigo fornecerá algumas informações adicionais sobre onde colocar a anotação: blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
bdoughan
3
Visto que o bloco crítico está protegido com "sincronizado", não há nenhum problema. Haverá problema (de desempenho) se várias chamadas forem feitas.
Mike Argyriou
17

Eu uso um SimpleDateFormat para criar o XMLGregorianCalendar, como neste exemplo:

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));
}

O primeiro método cria uma instância de XMLGregorianCalendar que é formatada pelo XML marshaller como um xsd: date válido, o segundo método resulta em um xsd: dateTime válido.

Andrea Luciano
fonte
2

Maneira muito fácil para mim. Formatando XMLGregorianCalendar para empacotamento em java.

Acabei de criar meus dados no bom formato. O toStringserá chamado produzindo o bom resultado.

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
fonte
1

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
fonte
0

Uso:

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);
    }
}
Mike
fonte