Conversión simple entre java.util.Date y XMLGregorianCalendar

110

Estoy buscando un método simple para convertir entre java.util.Date y javax.xml.datatype.XMLGregorianCalendar en ambas direcciones.

Aquí está el código que estoy usando ahora :

import java.util.GregorianCalendar;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

/**
 * Utility class for converting between XMLGregorianCalendar and java.util.Date
 */
public class XMLGregorianCalendarConverter {  

    /**
     * Needed to create XMLGregorianCalendar instances
     */
    private static DatatypeFactory df = null;
    static {
        try {
            df = DatatypeFactory.newInstance();
        } catch (DatatypeConfigurationException dce) {
            throw new IllegalStateException(
                "Exception while obtaining DatatypeFactory instance", dce);
        }
    }  

    /**
     * Converts a java.util.Date into an instance of XMLGregorianCalendar
     *
     * @param date Instance of java.util.Date or a null reference
     * @return XMLGregorianCalendar instance whose value is based upon the
     *  value in the date parameter. If the date parameter is null then
     *  this method will simply return null.
     */
    public static XMLGregorianCalendar asXMLGregorianCalendar(java.util.Date date) {
        if (date == null) {
            return null;
        } else {
            GregorianCalendar gc = new GregorianCalendar();
            gc.setTimeInMillis(date.getTime());
            return df.newXMLGregorianCalendar(gc);
        }
    }

    /**
     * Converts an XMLGregorianCalendar to an instance of java.util.Date
     *
     * @param xgc Instance of XMLGregorianCalendar or a null reference
     * @return java.util.Date instance whose value is based upon the
     *  value in the xgc parameter. If the xgc parameter is null then
     *  this method will simply return null.
     */
    public static java.util.Date asDate(XMLGregorianCalendar xgc) {
        if (xgc == null) {
            return null;
        } else {
            return xgc.toGregorianCalendar().getTime();
        }
    }
}

¿Hay algo más simple, como alguna llamada API que he pasado por alto?

La conversión entre una fecha / hora XML estándar y un objeto de fecha Java parece una tarea bastante rutinaria y me sorprende que tenga que escribir este código.

¿Alguna sugerencia?

NOTAS: Mis clases JAXB se generan automáticamente a partir de un esquema. El proceso de compilación de mi proyecto no me permite realizar cambios manuales en las clases generadas. XJC genera los elementos xs: dateTime como XMLGregorianCalendar en las clases JAXB. El esquema se amplía y modifica periódicamente, por lo que se me permite realizar cambios limitados en el archivo XSD del esquema.

ACTUALIZACIÓN DE LA SOLUCIÓN: La solución propuesta por Blaise me ha permitido sacar XMLGregorianCalendar de la mezcla y ocuparme de los objetos java.util.Calendar en su lugar. Al agregar una cláusula de enlace JAXB en la parte superior de mi archivo de esquema, XJC puede generar asignaciones más apropiadas para xs: dateTime en mis clases JAXB. Aquí hay algunos fragmentos que muestran las modificaciones en mi archivo XSD.

El elemento raíz en el archivo XSD:

<xs:schema xmlns:mydata="http://my.example.com/mydata" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" targetNamespace="http://my.example.com/mydata" elementFormDefault="unqualified" attributeFormDefault="unqualified" version="0.2" xml:lang="en" jaxb:version="2.0">

Bloque de anotación de enlace JAXB, insertado inmediatamente después del elemento raíz en XSD:

<xs:annotation>
    <xs:appinfo>
        <jaxb:globalBindings>
            <jaxb:javaType name="java.util.Calendar" xmlType="xs:dateTime" parseMethod="javax.xml.bind.DatatypeConverter.parseDateTime" printMethod="javax.xml.bind.DatatypeConverter.printDateTime" />
        </jaxb:globalBindings>
    </xs:appinfo>
</xs:annotation>

Dado que el campo XML xs: dateTime también almacena la zona horaria, podría ser mejor para mí trabajar con Calendar en lugar de Date de todos modos, ya que los objetos Calendar tienen una API bastante buena para trabajar con configuraciones regionales y zonas horarias. En cualquier caso, estoy mucho más feliz de tratar con objetos Calendar en lugar de XMLGregorianCalendar. Ya no es necesario los métodos de conversión que enumeré anteriormente. No llegué hasta java.util.Date, ¡pero lo suficientemente cerca!

Jim Tough
fuente
No conozco ninguno. Pero el tuyo parece bastante bueno, solo colócalo en un utilpaquete y úsalo.
Bozho
Una especie de aparte, pero ¿por qué tienes que lidiar con objetos XMLGregorianCalendar en primer lugar? Son un poco irritantes. Si vienen de jaxb, es posible usar @XMLTypeAdapter para enlazar directamente con java.util.Date. Por supuesto, si se está generando automáticamente a partir de un esquema, cambiar los objetos puede ser igual de irritante cuando se regenera.
Affe
@Affe Estoy generando automáticamente a partir de un esquema, por lo que no puedo realizar ningún cambio manual en las clases JAXB generadas
Jim Tough
¿Es lo mismo que stackoverflow.com/questions/835889/… ?
Jacob Tomaw
1
@Jacob - no lo es. Ya ha descubierto cómo hacerlo, se pregunta si no hay una clase de utilidad lista para usar.
Bozho

Respuestas:

46

¿Por qué no usar un archivo de enlace externo para decirle a XJC que genere campos java.util.Date en lugar de XMLGregorianCalendar?

Consulte también ¿Cómo asigno xs: date a java.util.Date? Blog

bdoughan
fuente
Voy a mirar en esto. Gracias.
Jim Tough
No hay problema. JAXB puede manejar el tipo java.util.Date, solo necesita generarlo en su modelo. Lo que puede ser complicado.
bdoughan
Eso funcionó para mí. Consulte las modificaciones de mi pregunta anterior para obtener detalles sobre lo que hice.
Jim Tough
Agregué enlaces jaxb pero justo debajo de xs: esquema y aparece el siguiente error: com.sun.istack.SAXParseException2: el compilador no pudo cumplir con esta personalización de enlaces globales. Está adherido a un lugar equivocado o es incompatible con otras fijaciones. en com.sun.tools.xjc.ErrorReceiver.error (ErrorReceiver.java:86) en ..
pri
@pritam: aquí hay otro ejemplo que puede ayudar: blog.bdoughan.com/2011/08/xml-schema-to-java-generating.html . Puede valer la pena hacer una nueva pregunta sobre el problema que está viendo.
bdoughan
81

De XMLGregorianCalendar a java.util.Date, simplemente puede hacer:

java.util.Date dt = xmlGregorianCalendarInstance.toGregorianCalendar().getTime();  
Zé Carlos
fuente
Gracias ... Estaba buscando una forma de convertir XMLGregorianCalendar a tiempo en milisegundos.
Andez
6

Desde java.util.Date a XMLGregorianCalendar, simplemente puede hacer:

import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.datatype.DatatypeFactory;
import java.util.GregorianCalendar;
......
GregorianCalendar gcalendar = new GregorianCalendar();
gcalendar.setTime(yourDate);
XMLGregorianCalendar xmlDate = DatatypeFactory.newInstance().newXMLGregorianCalendar(gcalendar);

Código editado después del primer comentario de @ f-puras, porque cometí un error.

moralejaSinCuentoNiProverbio
fuente
1
No funciona de la forma en que lo escribió: GregorianCalendar.setTime () no devolverá nada.
f_puras
5

Tuve que hacer algunos cambios para que funcionara, ya que algunas cosas parecen haber cambiado mientras tanto:

  • xjc se quejaría de que mi adaptador no extiende XmlAdapter
  • algunas importaciones extrañas e innecesarias fueron dibujadas en (org.w3._2001.xmlschema)
  • los métodos de análisis no deben ser estáticos al extender el XmlAdapter, obviamente

Aquí hay un ejemplo funcional, espero que esto ayude (estoy usando JodaTime pero en este caso SimpleDate sería suficiente):

import java.util.Date;
import javax.xml.bind.DatatypeConverter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.joda.time.DateTime;

public class DateAdapter extends XmlAdapter<Object, Object> {
    @Override
    public Object marshal(Object dt) throws Exception {
        return new DateTime((Date) dt).toString("YYYY-MM-dd");
    }

    @Override
        public Object unmarshal(Object s) throws Exception {
        return DatatypeConverter.parseDate((String) s).getTime();
    }
}

En el xsd, he seguido las excelentes referencias dadas anteriormente, por lo que he incluido esta anotación xml:

<xsd:appinfo>
    <jaxb:schemaBindings>
        <jaxb:package name="at.mycomp.xml" />
    </jaxb:schemaBindings>
    <jaxb:globalBindings>
        <jaxb:javaType name="java.util.Date" xmlType="xsd:date"
              parseMethod="at.mycomp.xml.DateAdapter.unmarshal"
          printMethod="at.mycomp.xml.DateAdapter.marshal" />
    </jaxb:globalBindings>
</xsd:appinfo>
Gregor
fuente
1
Me he convertido en un fan de Joda Time desde que hice esta pregunta. Mucho mejor que las clases de fecha y hora de Java SE. ¡Excelente para manejar zonas horarias!
Jim Tough
1

Yo también tenía este tipo de dolor de cabeza. Me deshice de él simplemente representando los campos de tiempo como primitivos largos en mi POJO. Ahora la generación de mi código de cliente WS maneja todo correctamente y no más basura de XML a Java. Y, por supuesto, tratar con millis en el lado de Java es simple e indoloro. ¡El principio de KISS es genial!

Alex
fuente
1

Puede utilizar esta personalización para cambiar la asignación predeterminada a java.util.Date

<xsd:annotation>
<xsd:appinfo>
    <jaxb:globalBindings>
        <jaxb:javaType name="java.util.Date" xmlType="xsd:dateTime"
                 parseMethod="org.apache.cxf.xjc.runtime.DataTypeAdapter.parseDateTime"
                 printMethod="org.apache.cxf.xjc.runtime.DataTypeAdapter.printDateTime"/>
    </jaxb:globalBindings>
</xsd:appinfo>

Dev
fuente
0

Personalización del calendario y la fecha durante la clasificación

Paso 1: Prepare jaxb binding xml para propiedades personalizadas, en este caso me preparé para la fecha y el calendario

<jaxb:bindings version="2.1" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" 
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jaxb:globalBindings generateElementProperty="false">
<jaxb:serializable uid="1" />
<jaxb:javaType name="java.util.Date" xmlType="xs:date"
    parseMethod="org.apache.cxf.tools.common.DataTypeAdapter.parseDate"
    printMethod="com.stech.jaxb.util.CalendarTypeConverter.printDate" />
<jaxb:javaType name="java.util.Calendar" xmlType="xs:dateTime"
    parseMethod="javax.xml.bind.DatatypeConverter.parseDateTime"
    printMethod="com.stech.jaxb.util.CalendarTypeConverter.printCalendar" />


Setp 2: agregue un archivo de enlace jaxb personalizado a Apache o cualquier complemento relacionado en la opción xsd como se menciona a continuación

<xsdOption>
  <xsd>${project.basedir}/src/main/resources/tutorial/xsd/yourxsdfile.xsd</xsd>
  <packagename>com.tutorial.xml.packagename</packagename>
  <bindingFile>${project.basedir}/src/main/resources/xsd/jaxbbindings.xml</bindingFile>
</xsdOption>

Setp 3: escriba el código para la clase CalendarConverter

package com.stech.jaxb.util;

import java.text.SimpleDateFormat;

/**
 * To convert the calendar to JaxB customer format.
 * 
 */

public final class CalendarTypeConverter {

    /**
     * Calendar to custom format print to XML.
     * 
     * @param val
     * @return
     */
    public static String printCalendar(java.util.Calendar val) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss");
        return simpleDateFormat.format(val.getTime());
    }

    /**
     * Date to custom format print to XML.
     * 
     * @param val
     * @return
     */
    public static String printDate(java.util.Date val) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        return simpleDateFormat.format(val);
    }
}

Setp 4: Salida

  <xmlHeader>
   <creationTime>2014-09-25T07:23:05</creationTime> Calendar class formatted

   <fileDate>2014-09-25</fileDate> - Date class formatted
</xmlHeader>
Kanthishere
fuente