¿Cómo se especifica el formato de fecha utilizado cuando JAXB calcula xsd: dateTime?

86

Cuando JAXB clasifica un objeto de fecha ( XMLGregorianCalendar) en un elemento xsd: dateTime. ¿Cómo puede especificar el formato del XML resultante?

Por ejemplo: el formato de datos predeterminado usa milisegundos <StartDate>2012-08-21T13:21:58.000Z</StartDate> , necesito omitir los milisegundos. <StartDate>2012-08-21T13:21:58Z</StartDate>

¿Cómo puedo especificar el formulario de salida / formato de fecha que quiero que use? Estoy usando javax.xml.datatype.DatatypeFactorypara crear el XMLGregorianCalendarobjeto.

XMLGregorianCalendar xmlCal = datatypeFactory.newXMLGregorianCalendar(cal);
Joven fu
fuente

Respuestas:

126

Puede utilizar un XmlAdapterpara personalizar cómo se escribe un tipo de fecha en 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);
        }
    }

}

Luego, usa la @XmlJavaTypeAdapteranotación para especificar que XmlAdapterdebe usarse para un campo / propiedad específico.

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

Usando un archivo de enlace xjb:

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

producirá la anotación mencionada anteriormente.
(Por finalmente añadiendo el xjcespacio de nombres: xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc")

bdoughan
fuente
2
¡Gracias por esta respuesta! ¿Es posible agregar la anotación a través de xsd o un archivo vinculante? Solo encontré su entrada de blog frecuentemente citada sobre bindings.xml, pero creo que esto cubre otros aspectos.
guerda
9
Como mencionó @PeterRader, SimpleDateFormat no es seguro para subprocesos: si dos subprocesos ingresaran al marshal o unmarshal simultáneamente, podría obtener resultados muy impredecibles. Esto sería muy difícil de reproducir en pruebas normales, pero podría ocurrir bajo carga y sería extremadamente difícil de diagnosticar. Es mejor crear un nuevo SimpleDateFormat con marshal y unmarshal (pero use una cadena de formato estático si es necesario).
Colselaw
1
Hice esto y casi funcionó. Sin embargo, recibí un Class has two properties of the same name "timeSeries"error: esto se resolvió colocando la anotación en el captador y no en el nivel de miembro. (Gracias a @megathor de stackoverflow.com/questions/6768544/… )
gordon613
1
@ gordon613 - Este artículo proporcionará información adicional sobre dónde colocar la anotación: blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
bdoughan
3
Dado que el bloque crítico está protegido con "sincronizado" no hay ningún problema. Habrá un problema (de rendimiento) si se realizan varias llamadas.
Mike Argyriou
17

Utilizo SimpleDateFormat para crear XMLGregorianCalendar, como en este ejemplo:

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

El primer método crea una instancia de XMLGregorianCalendar que el marshaller XML formatea como un xsd: date válido, el segundo método da como resultado un xsd: dateTime válido.

Andrea Luciano
fuente
2

Manera muy fácil para mí. Formateo de XMLGregorianCalendar para la clasificación en java.

Solo creo mis datos en buen formato. Se toStringllamará produciendo el buen 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
fuente
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);
    }
}
Miguel
fuente
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);
    }
}
Miguel
fuente