Prosta konwersja między java.util.Date i XMLGregorianCalendar

110

Szukam prostej metody konwersji między java.util.Date i javax.xml.datatype.XMLGregorianCalendar w obu kierunkach.

Oto kod, którego teraz używam :

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

Czy jest coś prostszego, na przykład wywołanie interfejsu API, które przeoczyłem?

Konwersja między standardową datą / godziną XML a obiektem daty Java wydaje się dość rutynowym zadaniem i jestem zaskoczony, że w ogóle muszę napisać ten kod.

Jakieś sugestie?

UWAGI: Moje klasy JAXB są generowane automatycznie ze schematu. Proces kompilacji w moim projekcie nie pozwala mi na ręczne wprowadzanie zmian w wygenerowanych klasach. Elementy xs: dateTime są generowane przez XJC jako XMLGregorianCalendar w klasach JAXB. Schemat jest okresowo rozszerzany i modyfikowany, więc mogę wprowadzać ograniczone zmiany w pliku XSD schematu.

AKTUALIZACJA ROZWIĄZANIA: Rozwiązanie zaproponowane przez Blaise'a pozwoliło mi wyeliminować XMLGregorianCalendar i zająć się obiektami java.util.Calendar. Dodając klauzulę powiązania JAXB na górze mojego pliku schematu, XJC jest w stanie wygenerować bardziej odpowiednie mapowania dla xs: dateTime w moich klasach JAXB. Oto kilka fragmentów, które pokazują modyfikacje w moim pliku XSD.

Element główny w pliku 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">

Blok adnotacji wiązania JAXB, wstawiany bezpośrednio po elemencie głównym w 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>

Ponieważ pole XML xs: dateTime również przechowuje strefę czasową, może lepiej byłoby dla mnie pracować z Kalendarzem zamiast z datą, ponieważ obiekty kalendarza mają całkiem niezłe API do pracy z ustawieniami narodowymi i strefami czasowymi. W każdym razie jestem o wiele szczęśliwszy, mogąc zajmować się obiektami Calendar zamiast XMLGregorianCalendar. Nie ma już potrzeby stosowania metod konwersji, które wymieniłem powyżej. Nie dotarłem do java.util.Date, ale wystarczająco blisko!

Jim Tough
źródło
Nie znam żadnego. Ale twój wydaje się całkiem niezły - po prostu umieść go w utilopakowaniu i użyj.
Bozho
Trochę na bok, ale dlaczego w pierwszej kolejności musisz zajmować się obiektami XMLGregorianCalendar? Są trochę irytujące. Jeśli pochodzą z jaxb, można użyć @XMLTypeAdapter do bezpośredniego powiązania z java.util.Date. Oczywiście, jeśli generujesz automatycznie poza schematem, zmiana obiektów może być równie irytująca podczas regeneracji.
Affe
@Affe Generuję automatycznie poza schematem, więc nie mogę dokonać żadnych ręcznych zmian w wygenerowanych klasach JAXB
Jim Tough
Czy to to samo, co stackoverflow.com/questions/835889/… ?
Jacob Tomaw
1
@Jacob - nie jest. Już wymyślił, jak to zrobić, zastanawia się, czy nie ma gotowej do użycia klasy użytkowej.
Bozho

Odpowiedzi:

46

Dlaczego nie użyć zewnętrznego pliku powiązania, aby powiedzieć XJC, aby generował pola java.util.Date zamiast XMLGregorianCalendar?

Zobacz także Jak zmapować xs: date na java.util.Date? Blog

bdoughan
źródło
Przyjrzę się temu. Dzięki.
Jim Tough
Nie ma problemu. JAXB obsługuje typ java.util.Date, wystarczy go wygenerować w swoim modelu. Co może być trudne.
bdoughan
To mi się udało. Zobacz zmiany w moim pytaniu powyżej, aby dowiedzieć się więcej o tym, co zrobiłem.
Jim Tough
Dodałem powiązania jaxb, ale tuż pod xs: schema i otrzymuję następujący błąd: com.sun.istack.SAXParseException2: kompilator nie był w stanie uwzględnić tego dostosowania globalBindings. Jest przymocowany w niewłaściwym miejscu lub jest niezgodny z innymi wiązaniami. w com.sun.tools.xjc.ErrorReceiver.error (ErrorReceiver.java:86) na ..
pri
@pritam - oto kolejny przykład, który może pomóc: blog.bdoughan.com/2011/08/xml-schema-to-java-generating.html . Warto zająć się nowym pytaniem dotyczącym napotkanego problemu.
bdoughan
81

Z XMLGregorianCalendar do java.util.Date możesz po prostu zrobić:

java.util.Date dt = xmlGregorianCalendarInstance.toGregorianCalendar().getTime();  
Zé Carlos
źródło
Dzięki ... Szukałem sposobu na przekonwertowanie XMLGregorianCalendar na czas w milisach.
Andez
6

Z java.util.Date do XMLGregorianCalendar możesz po prostu zrobić:

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

Kod zredagowany po pierwszym komentarzu @ f-puras, bo popełniam błąd.

moralejaSinCuentoNiProverbio
źródło
1
Nie działa tak, jak to napisałeś: GregorianCalendar.setTime () nic nie zwróci.
f_puras
5

Musiałem wprowadzić pewne zmiany, aby to działało, ponieważ w międzyczasie niektóre rzeczy się zmieniły:

  • xjc narzekałby, że mój adapter nie rozszerza XmlAdapter
  • niektóre dziwne i niepotrzebne importy zostały pobrane (org.w3._2001.xmlschema)
  • Oczywiście metody analizowania nie mogą być statyczne podczas rozszerzania XmlAdapter

Oto działający przykład, mam nadzieję, że to pomoże (używam JodaTime, ale w tym przypadku wystarczy SimpleDate):

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

W xsd zastosowałem się do doskonałych odniesień podanych powyżej, więc dołączyłem tę adnotację 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
źródło
1
Stałem się fanem Joda Time od czasu, gdy zadałem to pytanie. O wiele lepsze niż klasy daty i czasu Java SE. Niesamowite do obsługi stref czasowych!
Jim Tough
1

Ja też miałem tego rodzaju ból głowy. Pozbyłem się tego, po prostu przedstawiając pola czasu jako prymitywne długie w moim POJO. Teraz generowanie mojego kodu klienta WS obsługuje wszystko poprawnie i koniec z bzdurami z XML-to-Java. Oczywiście radzenie sobie z millisami po stronie Javy jest proste i bezbolesne. Zasada KISS rządzi!

Alex
źródło
1

Możesz użyć tego dostosowania, aby zmienić domyślne mapowanie na 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
źródło
0

Dostosowywanie kalendarza i daty podczas organizowania

Krok 1: Przygotuj plik XML wiążący jaxb dla właściwości niestandardowych, w tym przypadku przygotowałem datę i kalendarz

<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: Dodaj niestandardowy plik wiążący jaxb do Apache lub innych powiązanych wtyczek w opcji xsd, jak wspomniano poniżej

<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: napisz kod dla klasy 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);
    }
}

Nastawa 4: Wyjście

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

   <fileDate>2014-09-25</fileDate> - Date class formatted
</xmlHeader>
Kanthishere
źródło