2010-03-25 22 views
60

No consigo que JAXB marque una marca de tiempo en una aplicación de servidor Resteasy JAX-RS.jaxb unmarshal timestamp

Mi clase es el siguiente:

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

¿Alguna idea?

Gracias.

Respuesta

106

JAXB puede manejar la clase java.util.Date. Sin embargo se espera que el formato:

"aaaa-MM-dd'T'HH: mm: ss" en lugar de "aaaa-MM-dd hh: mm: ss"

Si desea utilizar esa fecha formato se recomienda usar un XmlAdapter, se vería algo como lo siguiente:

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

} 

a continuación, especifique este adaptador en su propiedad marca de tiempo:

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

} 
+1

De hecho, descubrí cómo hacer la traducción utilizando @XmlJavaTypeAdapter. Con esto, he podido adaptar varias clases de Java diferentes que requieren un tratamiento especial en mi código. Gracias. – Ralph

+5

¡Debe tener mucho cuidado con SimpleDateformat, ya que no es seguro para la rosca! No estoy seguro si JAXB crea un nuevo objeto cada vez para el adaptador. –

+0

Para obtener más información, consulte: http://bdoughan.blogspot.com/2011/01/jaxb-and-datetime-properties.html –

10

JAXB no puede ordenar objetos Date directamente, porque no tienen suficiente información para ser inequívocos. JAXB introdujo la clase XmlGregorianCalendar para este propósito, pero es muy desagradable de usar directamente.

Sugiero que cambie su campo timestamp para que sea XmlGregorianCalendar, y cambie sus diversos métodos para actualizar este campo conservando la interfaz pública que ya tiene, siempre que sea posible.

Si desea mantener el campo Date, entonces usted necesita para implementar su propia clase XmlAdapter para contar JAXB a cómo convertir su Date hacia y desde XML.

+2

JAXB ciertamente puede ordenar java.util.Date, solo el formato es "aaaa-MM-ddTHH: mm: ss" que coincide con xsd: dateTime. La implementación de EclipseLink JAXB (MOXy) también puede manejar los tipos java.sql.Date/Time/Timestamp. –

+2

@Blaise: Me corrigen – skaffman

3

con el fin de conseguir el contador de referencias XML para generar una xsd: fecha f ormatted como AAAA-MM-DD sin definir un XmlAdapter I utilizado este método para construir una instancia de 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)); 
} 

Con el resultado I inicializado el campo XMLGregorianCalendar de la clase generada por JAXB compilador (en 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); 

y obtuvo la etiqueta formateada como se esperaba:

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

el uso de este adaptador debe ser seguro de rosca:

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

} 
+0

Recibí el siguiente error: org.xml.sax.SAXParseException ; lineNumber: 0; columnNumber: 0; cvc-datatype-valid.1.2.1: '05/10/2017 00:00:00 'no es un valor válido para' dateTime ' – Aguid

+0

su cadena '05/10/2017 00:00:00' no tiene el formato esperado: "aaaa-MM-dd'T'HH: mm: ss" - debe cambiar la entrada o el formato – dermoritz

+0

INFRAXML 2.2: javax.xml.bind.MarshalException - con la excepción vinculada: [org.xml.sax. SAXParseException; lineNumber: 0; columnNumber: 0; cvc-datatype-valid.1.2.1: '2017-10-10T14: 59: 34' no es un valor válido para 'fecha'.] – Aguid