Conversione semplice tra java.util.Date e XMLGregorianCalendar


110

Sto cercando un metodo semplice per convertire tra java.util.Date e javax.xml.datatype.XMLGregorianCalendar in entrambe le direzioni.

Ecco il codice che sto usando ora :

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

C'è qualcosa di più semplice, come alcune chiamate API che ho trascurato?

La conversione tra una data / ora XML standard e un oggetto data Java sembra un'attività piuttosto di routine e sono sorpreso di dover scrivere questo codice.

Eventuali suggerimenti?

NOTE: le mie classi JAXB vengono generate automaticamente da uno schema. Il processo di compilazione del mio progetto non mi consente di apportare modifiche manuali alle classi generate. Gli elementi xs: dateTime vengono generati da XJC come XMLGregorianCalendar nelle classi JAXB. Lo schema viene esteso e ottimizzato periodicamente, quindi posso apportare modifiche limitate al file XSD dello schema.

AGGIORNAMENTO SULLA SOLUZIONE: La soluzione proposta da Blaise mi ha permesso di togliere XMLGregorianCalendar dal mix e di trattare invece gli oggetti java.util.Calendar. Aggiungendo una clausola di associazione JAXB all'inizio del file dello schema, XJC è in grado di generare mappature più appropriate per xs: dateTime nelle mie classi JAXB. Ecco alcuni frammenti che mostrano le modifiche nel mio file XSD.

L'elemento radice nel file 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">

Blocco di annotazione vincolante JAXB, inserito immediatamente dopo l'elemento radice in 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>

Poiché il campo XML xs: dateTime memorizza anche il fuso orario, potrebbe essere meglio per me lavorare comunque con Calendar anziché con Date poiché gli oggetti Calendar hanno un'API piuttosto buona per lavorare con le impostazioni locali e i fusi orari. In ogni caso, sono molto più felice di occuparmi di oggetti Calendar invece di XMLGregorianCalendar. Non sono più necessari i metodi di conversione che ho elencato sopra. Non sono arrivato fino a java.util.Date, ma abbastanza vicino!


Non ne conosco nessuno. Ma il tuo sembra abbastanza a posto: mettilo in un utilpacchetto e usalo.
Bozho

Un po 'a parte, ma perché hai a che fare con gli oggetti XMLGregorianCalendar in primo luogo? Sono un po 'irritanti. Se provengono da jaxb, è possibile utilizzare @XMLTypeAdapter per collegarsi direttamente a java.util.Date. Ovviamente se stai generando automaticamente da uno schema, cambiare gli oggetti può essere altrettanto irritante quando ti rigeneri.
Affe

@Affe Sto generando automaticamente da uno schema, quindi non posso apportare modifiche manuali alle classi JAXB generate
Jim Tough


1
@Jacob - non lo è. Ha già capito come farlo, si chiede se non esiste una classe di utilità pronta per l'uso.
Bozho

Risposte:


46

Perché non utilizzare un file di binding esterno per dire a XJC di generare i campi java.util.Date invece di XMLGregorianCalendar?

Vedi anche Come associo xs: date a java.util.Date? blog


Lo esaminerò. Grazie.
Jim Tough,

Nessun problema. JAXB può gestire il tipo java.util.Date, devi solo generarlo nel tuo modello. Che può essere complicato.
bdoughan

Ha funzionato per me. Vedi le modifiche alla mia domanda sopra per i dettagli su quello che ho fatto.
Jim Tough

Ho aggiunto i collegamenti jaxb ma subito sotto xs: schema e ottengo il seguente errore: com.sun.istack.SAXParseException2: il compilatore non è stato in grado di rispettare questa personalizzazione di globalBindings. È attaccato a un posto sbagliato o è incoerente con altri attacchi. su com.sun.tools.xjc.ErrorReceiver.error (ErrorReceiver.java:86) su ..
pri

@pritam - Ecco un altro esempio che può aiutare: blog.bdoughan.com/2011/08/xml-schema-to-java-generating.html . Potrebbe valere la pena porre una nuova domanda per il problema che stai riscontrando.
bdoughan

81

Da XMLGregorianCalendar a java.util.Date puoi semplicemente fare:

java.util.Date dt = xmlGregorianCalendarInstance.toGregorianCalendar().getTime();  

Grazie ... stavo cercando un modo per convertire XMLGregorianCalendar in tempo in millisecondi.
Andez

6

Da java.util.Date a XMLGregorianCalendar puoi semplicemente fare:

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

Codice modificato dopo il primo commento di @ f-puras, perché faccio un errore.


1
Non funziona come l'hai scritto: GregorianCalendar.setTime () non restituirà nulla.
f_puras

5

Ho dovuto apportare alcune modifiche per farlo funzionare, poiché alcune cose sembrano essere cambiate nel frattempo:

  • xjc si lamenterebbe del fatto che il mio adattatore non estende XmlAdapter
  • alcune importazioni bizzarre e non necessarie sono state prelevate (org.w3._2001.xmlschema)
  • i metodi di analisi non devono essere statici quando si estende XmlAdapter, ovviamente

Ecco un esempio funzionante, spero che questo aiuti (sto usando JodaTime ma in questo caso SimpleDate sarebbe sufficiente):

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

In xsd, ho seguito gli eccellenti riferimenti sopra riportati, quindi ho incluso questa annotazione 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>

1
Sono diventato un fan di Joda Time da quando ho posto questa domanda. Molto meglio delle classi di data e ora di Java SE. Fantastico per gestire i fusi orari!
Jim Tough

1

Anch'io avevo questo tipo di mal di testa. Me ne sono sbarazzato semplicemente rappresentando i campi del tempo come lunghi primitivi nel mio POJO. Ora la generazione del mio codice client WS gestisce tutto correttamente e non più cazzate da XML a Java. E ovviamente trattare con i millisecondi sul lato Java è semplice e indolore. Il principio KISS è fantastico!


1

È possibile utilizzare questa personalizzazione per modificare la mappatura predefinita in 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>


0

Personalizzazione del calendario e della data durante il marshalling

Passaggio 1: preparare jaxb binding xml per proprietà personalizzate, in questo caso ho preparato per data e 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: aggiungi il file di binding jaxb personalizzato ad Apache o qualsiasi plug-in correlato all'opzione xsd come indicato di seguito

<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: scrivi il codice per la classe 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: Uscita

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

   <fileDate>2014-09-25</fileDate> - Date class formatted
</xmlHeader>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.