Konversi sederhana antara java.util.Date dan XMLGregorianCalendar

110

Saya mencari metode sederhana untuk mengubah antara java.util.Date dan javax.xml.datatype.XMLGregorianCalendar di kedua arah.

Ini kode yang saya gunakan sekarang :

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

Apakah ada yang lebih sederhana, seperti beberapa panggilan API yang telah saya abaikan?

Mengubah antara tanggal / waktu XML standar dan objek tanggal Java tampaknya seperti tugas yang cukup rutin dan saya terkejut bahwa saya harus menulis kode ini sama sekali.

Ada saran?

CATATAN: Kelas JAXB saya dibuat secara otomatis dari skema. Proses membangun proyek saya tidak memungkinkan saya untuk membuat perubahan manual pada kelas yang dibuat. Elemen xs: dateTime sedang dibuat oleh XJC sebagai XMLGregorianCalendar di kelas JAXB. Skema diperpanjang dan diubah secara berkala, jadi saya diizinkan untuk membuat perubahan terbatas pada file skema XSD.

UPDATE ON SOLUTION: Solusi yang diusulkan oleh Blaise memungkinkan saya untuk mengeluarkan XMLGregorianCalendar dari campuran dan menangani objek java.util.Calendar sebagai gantinya. Dengan menambahkan klausa pengikatan JAXB di bagian atas file skema saya, XJC dapat menghasilkan pemetaan yang lebih sesuai untuk xs: dateTime di kelas JAXB saya. Berikut beberapa cuplikan yang menunjukkan modifikasi pada file XSD saya.

Elemen root di 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">

Blok anotasi pengikatan JAXB, dimasukkan segera setelah elemen root di 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>

Karena bidang XML xs: dateTime juga menyimpan zona waktu, mungkin lebih baik bagi saya untuk bekerja dengan Kalender daripada Tanggal karena objek Kalender memiliki API yang cukup bagus untuk bekerja dengan lokal dan zona waktu. Bagaimanapun, saya jauh lebih senang menangani objek Kalender daripada XMLGregorianCalendar. Tidak perlu lagi metode konversi yang saya cantumkan di atas. Saya tidak mengerti sepenuhnya java.util.Date, tapi cukup dekat!

Jim Tough
sumber
Saya tidak tahu satupun. Tapi milik Anda tampaknya baik-baik saja - cukup letakkan dalam satu utilpaket dan gunakan.
Bozho
Semacam samping, tapi mengapa Anda harus berurusan dengan objek XMLGregorianCalendar di tempat pertama? Mereka agak menjengkelkan. Jika mereka datang dari jaxb, Anda dapat menggunakan @XMLTypeAdapter untuk mengikat langsung ke java.util.Date. Tentu saja jika Anda melakukan autogenerasi keluar dari skema, mengubah objek bisa sama menjengkelkannya saat Anda membuat ulang.
Affe
@Affe Saya membuat otomatis dari skema jadi saya tidak dapat membuat perubahan manual apa pun ke kelas JAXB yang dihasilkan
Jim Tough
Apakah ini sama dengan stackoverflow.com/questions/835889/… ?
Jacob Tomaw
1
@ Jacob - tidak. Dia sudah menemukan cara melakukannya, dia bertanya-tanya apakah tidak ada kelas utilitas yang siap digunakan.
Bozho

Jawaban:

46

Mengapa tidak menggunakan file pengikat eksternal untuk memberi tahu XJC agar menghasilkan bidang java.util.Date alih-alih XMLGregorianCalendar?

Lihat juga Bagaimana cara memetakan xs: date ke java.util.Date? Blog

bdoughan.dll
sumber
Saya akan menyelidiki ini. Terima kasih.
Jim Tough
Tidak masalah. JAXB dapat menangani jenis java.util.Date, Anda hanya perlu membuatnya di model Anda. Yang bisa jadi rumit.
bdoughan
Itu berhasil bagi saya. Lihat edit pertanyaan saya di atas untuk detail tentang apa yang saya lakukan.
Jim Tough
Saya menambahkan jaxb binding tetapi tepat di bawah xs: schema dan saya mendapatkan error berikut: com.sun.istack.SAXParseException2: compiler tidak dapat menerima kustomisasi globalBindings ini. Itu melekat pada tempat yang salah, atau tidak konsisten dengan binding lain. di com.sun.tools.xjc.ErrorReceiver.error (ErrorReceiver.java:86) di ..
pri
@pritam - Berikut adalah contoh lain yang mungkin membantu: blog.bdoughan.com/2011/08/xml-schema-to-java-generating.html . Mungkin ada baiknya mengajukan pertanyaan baru untuk masalah yang Anda lihat.
bdoughan
81

Dari XMLGregorianCalendar ke java.util.Date Anda cukup melakukan:

java.util.Date dt = xmlGregorianCalendarInstance.toGregorianCalendar().getTime();  
Zé Carlos
sumber
Terima kasih ... Saya sedang mencari cara untuk mengonversi XMLGregorianCalendar ke waktu dalam milidetik.
Andez
6

Dari java.util.Date ke XMLGregorianCalendar Anda cukup melakukan:

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

Kode diedit setelah komentar pertama @ f-puras, karena saya melakukan kesalahan.

moralejaSinCuentoNiProverbio
sumber
1
Tidak berfungsi seperti yang Anda tulis: GregorianCalendar.setTime () tidak akan mengembalikan apa pun.
f_puras
5

Saya harus membuat beberapa perubahan untuk membuatnya berfungsi, karena beberapa hal tampaknya telah berubah untuk sementara waktu:

  • xjc akan mengeluh bahwa adaptor saya tidak memperluas XmlAdapter
  • beberapa impor yang aneh dan tidak diperlukan ditarik ke (org.w3._2001.xmlschema)
  • metode parsing tidak boleh statis saat memperluas XmlAdapter, tentunya

Berikut adalah contoh yang berfungsi, semoga ini membantu (saya menggunakan JodaTime tetapi dalam hal ini SimpleDate sudah cukup):

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

Di xsd, saya telah mengikuti referensi luar biasa yang diberikan di atas, jadi saya telah menyertakan anotasi xml ini:

<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
sumber
1
Saya telah menjadi penggemar Joda Time sejak saya mengajukan pertanyaan ini. Jauh lebih baik daripada kelas tanggal dan waktu Java SE. Luar biasa untuk menangani zona waktu!
Jim Tough
1

Saya juga mengalami sakit kepala seperti ini. Singkirkan itu hanya dengan mewakili bidang waktu sebagai primitif lama di POJO saya. Sekarang generasi kode klien WS saya menangani semuanya dengan benar dan tidak ada lagi omong kosong XML-ke-Java. Dan tentu saja berurusan dengan millis di sisi Java itu sederhana dan tidak menyakitkan. Prinsip KISS rock!

Alex
sumber
1

Anda dapat menggunakan penyesuaian ini untuk mengubah pemetaan default ke 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
sumber
0

Menyesuaikan Kalender dan Tanggal saat Marshaling

Langkah 1: Siapkan jaxb binding xml untuk properti kustom, dalam hal ini saya menyiapkan tanggal dan kalender

<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: Tambahkan file jaxb binding kustom ke Apache atau plugin terkait di opsi xsd seperti yang disebutkan di bawah ini

<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: tulis kode untuk kelas 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: Keluaran

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

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