Conversion simple entre java.util.Date et XMLGregorianCalendar

110

Je recherche une méthode simple de conversion entre java.util.Date et javax.xml.datatype.XMLGregorianCalendar dans les deux sens.

Voici le code que j'utilise maintenant :

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

Y a-t-il quelque chose de plus simple, comme un appel d'API que j'ai négligé?

La conversion entre une date / heure XML standard et un objet de date Java semble être une tâche assez routinière et je suis surpris de devoir écrire ce code.

Aucune suggestion?

REMARQUES: Mes classes JAXB sont générées automatiquement à partir d'un schéma. Le processus de construction de mon projet ne me permet pas d'apporter des modifications manuelles aux classes générées. Les éléments xs: dateTime sont générés par XJC en tant que XMLGregorianCalendar dans les classes JAXB. Le schéma est étendu et modifié périodiquement, je suis donc autorisé à apporter des modifications limitées au fichier XSD de schéma.

MISE À JOUR SUR LA SOLUTION: La solution proposée par Blaise m'a permis de sortir XMLGregorianCalendar du mix et de traiter les objets java.util.Calendar à la place. En ajoutant une clause de liaison JAXB en haut de mon fichier de schéma, XJC est capable de générer des mappages plus appropriés pour xs: dateTime dans mes classes JAXB. Voici quelques extraits qui montrent les modifications dans mon fichier XSD.

L'élément racine dans le fichier 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">

Bloc d'annotation de liaison JAXB, inséré immédiatement après l'élément racine dans 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>

Étant donné que le champ XML xs: dateTime stocke également le fuseau horaire, il serait peut-être préférable pour moi de travailler avec Calendar au lieu de Date de toute façon, car les objets Calendar ont une assez bonne API pour travailler avec les paramètres régionaux et les fuseaux horaires. Dans tous les cas, je suis beaucoup plus heureux de traiter les objets Calendar au lieu de XMLGregorianCalendar. Plus besoin des méthodes de conversion que j'ai énumérées ci-dessus. Je ne suis pas arrivé à java.util.Date, mais assez près!

Jim Tough
la source
Je n'en connais aucun. Mais le vôtre semble assez bien - placez-le simplement dans un utilemballage et utilisez-le.
Bozho
Une sorte de aparté, mais pourquoi devez-vous traiter les objets XMLGregorianCalendar en premier lieu? Ils sont un peu irritants. S'ils proviennent de jaxb, il est possible d'utiliser @XMLTypeAdapter pour se lier directement à java.util.Date. Bien sûr, si vous générez automatiquement à partir d'un schéma, la modification des objets peut être tout aussi irritante lors de la régénération.
Affe le
@Affe Je génère automatiquement à partir d'un schéma, donc je ne peux pas apporter de modifications manuelles aux classes JAXB générées
Jim Tough
Est-ce la même chose que stackoverflow.com/questions/835889/... ?
Jacob Tomaw
1
@Jacob - ce n'est pas le cas. Il a déjà compris comment le faire, il se demande s'il n'y a pas de classe utilitaire prête à l'emploi.
Bozho

Réponses:

46

Pourquoi ne pas utiliser un fichier de liaison externe pour indiquer à XJC de générer des champs java.util.Date au lieu de XMLGregorianCalendar?

Voir également Comment mapper xs: date à java.util.Date? Blog

bdoughan
la source
J'examinerai ceci. Merci.
Jim Tough
Aucun problème. JAXB peut gérer le type java.util.Date, il vous suffit de le générer dans votre modèle. Ce qui peut être délicat.
bdoughan le
Cela a fonctionné pour moi. Voir les modifications apportées à ma question ci-dessus pour plus de détails sur ce que j'ai fait.
Jim Tough
J'ai ajouté des liaisons jaxb mais juste en dessous de xs: schema et j'obtiens l'erreur suivante: com.sun.istack.SAXParseException2: le compilateur n'a pas pu honorer cette personnalisation globalBindings. Il est attaché à un mauvais endroit ou est incompatible avec d'autres liaisons. à com.sun.tools.xjc.ErrorReceiver.error (ErrorReceiver.java:86) à ..
pri
@pritam - Voici un autre exemple qui peut aider: blog.bdoughan.com/2011/08/xml-schema-to-java-generating.html . Cela peut valoir la peine de poser une nouvelle question pour le problème que vous rencontrez.
bdoughan
81

De XMLGregorianCalendar à java.util.Date, vous pouvez simplement faire:

java.util.Date dt = xmlGregorianCalendarInstance.toGregorianCalendar().getTime();  
Zé Carlos
la source
Merci ... Je cherchais un moyen de convertir XMLGregorianCalendar en temps en millis.
Andez
6

De java.util.Date à XMLGregorianCalendar, vous pouvez simplement faire:

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

Code édité après le premier commentaire de @ f-puras, parce que je fais une erreur.

moraljaSinCuentoNiProverbio
la source
1
Ne fonctionne pas comme vous l'avez écrit: GregorianCalendar.setTime () ne retournera rien.
f_puras
5

J'ai dû faire quelques changements pour que cela fonctionne, car certaines choses semblent avoir changé entre-temps:

  • xjc se plaindrait que mon adaptateur n'étend pas XmlAdapter
  • des importations bizarres et inutiles ont été attirées dans (org.w3._2001.xmlschema)
  • les méthodes d'analyse ne doivent pas être statiques lors de l'extension de XmlAdapter, évidemment

Voici un exemple de travail, j'espère que cela aide (j'utilise JodaTime mais dans ce cas SimpleDate serait suffisant):

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

Dans le xsd, j'ai suivi les excellentes références données ci-dessus, j'ai donc inclus cette annotation 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
la source
1
Je suis devenu fan de Joda Time depuis que j'ai posé cette question. Tellement mieux que les classes de date et d'heure Java SE. Génial pour gérer les fuseaux horaires!
Jim Tough
1

J'avais aussi ce genre de mal de tête. Je m'en débarrasse en représentant simplement les champs temporels comme un long primitif dans mon POJO. Maintenant, la génération de mon code client WS gère tout correctement et plus de merde XML vers Java. Et bien sûr, gérer les millis du côté Java est simple et indolore. Le principe de KISS est génial!

Alex
la source
1

Vous pouvez utiliser cette personnalisation pour modifier le mappage par défaut en 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
la source
0

Personnalisation du calendrier et de la date lors du marshaling

Étape 1: préparer jaxb binding xml pour les propriétés personnalisées, dans ce cas, je me suis préparé pour la date et le calendrier

<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: Ajouter un fichier de liaison jaxb personnalisé à Apache ou à tout plug-in associé à l'option xsd comme mentionné ci-dessous

<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: écrivez le code de 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: Sortie

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

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