在 java.util.Date 和 XMLGregorianCalendar 之间进行简单的转换

我正在寻找一种在 java.util.Date 和 javax.xml.datatype.XMLGregorianCalendar 之间双向转换的简单方法。

下面是我现在使用的代码 :

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

还有比这更简单的吗,比如我忽略的一些 API 调用?

在标准的 XML 日期/时间和 Java 日期对象之间进行转换似乎是一个相当常规的任务,我很惊讶自己竟然还要编写这样的代码。

有什么建议吗?

备注: 我的 JAXB 类是从模式自动生成的。我的项目上的构建过程不允许我对生成的类进行手动更改。DateTime 元素由 XJC 作为 JAXB 类中的 XMLGregorianCalendar 生成。模式被定期扩展和调整,因此我可以对模式 XSD 文件进行有限的更改。

解决方案更新: Blaise 提出的解决方案允许我将 XMLGregorianCalendar 排除在外并处理 java.util。取而代之的是日历对象。通过在模式文件的顶部添加 JAXB 绑定子句,XJC 能够在 JAXB 类中为 xs: dateTime 生成更合适的映射。下面是一些片段,它们显示了我的 XSD 文件中的修改。

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

JAXB 绑定注释块,插入 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>

由于 XML xs: dateTime 字段也存储时区,因此对我来说使用 Calendar 而不是 Date 可能更好,因为 Calendar 对象有一个非常好的 API 用于处理区域和时区。无论如何,我更愿意处理 Calendar 对象而不是 XMLGregorianCalendar。不再需要我上面列出的转换方法。我没有去爪哇直到。约会,但也差不多了!

193182 次浏览

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

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

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

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.

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>

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>