ChatGPT解决这个技术问题 Extra ChatGPT

Simple conversion between java.util.Date and XMLGregorianCalendar

I'm looking for a simple method of converting between java.util.Date and javax.xml.datatype.XMLGregorianCalendar in both directions.

Here is the code that I'm using now:

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

Is there anything simpler, like some API call that I have overlooked?

Converting between a standard XML date/time and a Java date object seems like a pretty routine task and I'm surprised that I have to write this code at all.

Any suggestions?

NOTES: My JAXB classes are autogenerated from a schema. The build process on my project does not allow for me to make manual changes to the generated classes. The xs:dateTime elements are being generated by XJC as XMLGregorianCalendar in the JAXB classes. The schema is extended and tweaked periodically, so I am allowed to make limited changes to the schema XSD file.

UPDATE ON SOLUTION: The solution proposed by Blaise has allowed me to take XMLGregorianCalendar out of the mix and deal with java.util.Calendar objects instead. By adding a JAXB binding clause at the top of my schema file, XJC is able to generate more appropriate mappings for xs:dateTime in my JAXB classes. Here are some snippets that show the modifications in my XSD file.

The root element in the XSD file:

<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">

JAXB binding annotation block, inserted immediately after root element 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>

Since the XML xs:dateTime field also stores timezone, it might be better for me to work with Calendar instead of Date anyway since Calendar objects have a pretty good API for working with locales and timezones. In any case, I'm much happier to deal with Calendar objects instead of XMLGregorianCalendar. No need for the conversion methods that I listed above anymore. I didn't get all the way to java.util.Date, but close enough!

I don't know of any. But yours seems pretty fine - just place it in a util package and use it.
Sort of an aside, but why do you have to deal with XMLGregorianCalendar objects in the first place? They're kind of irritating. If they're coming from jaxb it is possible to use @XMLTypeAdapter to bind directly to java.util.Date. Of course if you're autogenerating off of a schema, changing the objects can be just as irritating when you regenerate.
@Affe I'm autogenerating off of a schema so I can't make any manual changes to the generated JAXB classes
@Jacob - it is not. He has already figured out how to do it, he wonders whether there isn't a ready-to-use utility class.

Z
Zé Carlos

From XMLGregorianCalendar to java.util.Date you can simply do:

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

Thanks... I was looking for a way to convert XMLGregorianCalendar to time in millis.
M
Mahmoud Khaled

Why not use an external binding file to tell XJC to generate java.util.Date fields instead of XMLGregorianCalendar?

Also see How do I map xs:date to java.util.Date? Blog


I will look into this. Thanks.
No problem. JAXB can handle the java.util.Date type, you just need to generate it in your model. Which can be tricky.
That worked out for me. See edits to my question above for details on what I did.
I added jaxb bindings but right below xs:schema and I get the following error: com.sun.istack.SAXParseException2: compiler was unable to honor this globalBindings customization. It is attached to a wrong place, or its inconsistent with other bindings. at com.sun.tools.xjc.ErrorReceiver.error(ErrorReceiver.java:86) at ..
@pritam - Here is another example that may help: blog.bdoughan.com/2011/08/xml-schema-to-java-generating.html. It may be worth staring a new question for the issue you are seeing.
m
moralejaSinCuentoNiProverbio

From java.util.Date to XMLGregorianCalendar you can simply do:

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 edited after the first comment of @f-puras, by cause i do a mistake.


Does not work the way you wrote it: GregorianCalendar.setTime() will not return anything.
G
Gregor

I had to make some changes to make it work, as some things seem to have changed in the meantime:

xjc would complain that my adapter does not extend XmlAdapter

some bizarre and unneeded imports were drawn in (org.w3._2001.xmlschema)

the parsing methods must not be static when extending the XmlAdapter, obviously

Here's a working example, hope this helps (I'm using JodaTime but in this case SimpleDate would be sufficient):

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 { @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 the xsd, I have followed the excellent references given above, so I have included this xml annotation:

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

I have become a Joda Time fan in the time since I posed this question. So much better than the Java SE date and time classes. Awesome for handling timezones!
A
Alex

I too had this kind of headache. Got rid of of it by simply representing time fields as primitive long in my POJO. Now the generation of my WS client code handle everything correctly and no more XML-to-Java crap. And of course dealing with millis on the Java side is simple and painless. KISS principle rocks!


D
Dev

You can use the this customization to change the default mapping to 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>


A
Amit Bhatiya

Customizing the Calendar and Date while Marshaling

Step 1 : Prepare jaxb binding xml for custom properties, In this case i prepared for date and calendar

<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 : Add custom jaxb binding file to Apache or any related plugins at xsd option like mentioned below

<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 : write the code for CalendarConverter class

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 : Output

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

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