XMLGregorianCalendar日期序列化为空字符串

时间:2011-12-09 16:06:15

标签: java validation jax-ws

使用Java 1.6 wsimport我从WSDL为Web服务生成源代码。请求结构中的一个字段在WSDL包含的XML模式中具有类型xs:dateTime,并在生成的代码中键入javax.xml.datatype.XMLGregorianCalendar

通过soapUI手动测试,我确定Web服务接受以下序列化值:2011-12-082011-12-08Z。以下内容未被接受,本案例中的回复是空回复(不是明确错误):2011-12-08T20:00:002011-12-08T20:00:00-05:00。如果重要的话,服务本身就是.NET驱动的。

我的想法是服务器应该接受完整的日期/时间并拒绝日期,但反过来就是发生了什么。但我不假设服务器的维护者可以改变。所以我试图说服客户只发送日期。

我无法说服我的客户端代码将XMLGregorianCalendar对象序列化为仅日期。实际上我可以,除了,当生成的代码执行它时。当生成的客户端代码(由wsimport生成)执行此操作时,序列化值为空字符串,并且服务器正确返回错误。我使用数据包嗅探器验证了这一点。

以下是我在请求中创建和填充日期字段的方法:

import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import java.util.TimeZone;
// also import GeneratedRequest from generated packages

private makeRequest() {
   GeneratedRequest request;
   // ...
   request.setDateField(xmlDayNow(TimeZone.getTimeZone("America/New_York"),
       6));  // broadcast day starts at 6 am EST
   // ...
}

@XmlSchemaType(name="date")
private static XMLGregorianCalendar xmlDayNow(TimeZone tz, int localHourStart)
throws MyException {
    GregorianCalendar cal = gregorianBroadcastDayNow(tz, localHourStart);
    XMLGregorianCalendar result;
    try {
        result = DatatypeFactory.newInstance().newXMLGregorianCalendarDate(
            cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1,
            cal.get(Calendar.DAY_OF_MONTH), DatatypeConstants.FIELD_UNDEFINED)
            .normalize();
    } catch (DatatypeConfigurationException e) {
        throw new MyException("XMLGregorianCalendar issue", e);
    }
    return result;
}

protected static GregorianCalendar gregorianBroadcastDayNow(TimeZone tz,
        int localHourStart) {
    GregorianCalendar now = new GregorianCalendar(tz);
    if (now.get(GregorianCalendar.HOUR_OF_DAY) < localHourStart) {
        now.add(GregorianCalendar.DAY_OF_MONTH, -1);
    }
    return now;
}

在我的例子中,XMLGregorianCalendar的实现类是com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl。在调试器中,或者如果我添加日志输出,则调用日期对象的toXMLFormat()方法仅返回日期,例如2011-12-09。使用调试器检查日期对象本身,我看到它填充了yeardaymonth字段,其他所有字段都是null或{{1这是-2147483648的值。根据我发现的所有文档和Internet搜索结果,我的日期对象正确形成。

我疯了吗?服务器真的出错吗?拒绝生成的客户端代码只发送日期是否正确?这是一个合理的“未定义行为”案例吗?是否使用了错误的实现类(无论如何可能都很重要)? DatatypeConstants.FIELD_UNDEFINED是否存在一些影响我的已知问题?

3 个答案:

答案 0 :(得分:3)

我发现JAXB的日期转换不应该留给他们自己的设备。不熟悉wsimport,但它是否允许您指定绑定文件?我使用Joda日期/时间,但我的确是这个想法是一样的。将此添加到我的binding.xjb:

<globalBindings>
    <serializable />
    <javaType name="org.joda.time.DateTime" xmlType="xsd:dateTime"
        printMethod="someStaticDateConverterClass.printDateIso"
        parseMethod="someStaticDateConverterClass.parseDateIso" />
    <javaType name="org.joda.time.DateTime" xmlType="xs:date"
        printMethod="someStaticDateConverterClass.printDateYYYYDashMMDashDD"
        parseMethod="someStaticDateConverterClass.parseDateYYYYDashMMDashDD" />
</globalBindings>

这样的课程:

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;

public class someStaticDateConverterClass{

private static DateTimeFormatter isoFormat = ISODateTimeFormat.dateTime();
private static DateTimeFormatter YYYYDashMMDashDDFormat = DateTimeFormat
        .forPattern("yyyy-MM-dd");
private static DateTimeFormatter YYYYMMDDFormat = DateTimeFormat
        .forPattern("yyyyMMdd");

public static String printDateIso(DateTime value) {
    return printDate(value, isoFormat);
}

public static DateTime parseDateIso(String value) {
    return parseDate(value, isoFormat);
}

public static String printDateYYYYDashMMDashDD(DateTime value) {
    return printDate(value, YYYYDashMMDashDDFormat);
}

public static DateTime parseDateYYYYDashMMDashDD(String value) {
    return parseDate(value, YYYYDashMMDashDDFormat);
}

public static String printDateYYYYMMDD(DateTime value) {
    return printDate(value, YYYYMMDDFormat);
}

public static DateTime parseDateYYYYMMDD(String value) {
    return parseDate(value, YYYYMMDDFormat);
}

private static String printDate(DateTime value, DateTimeFormatter format) {

    String dateAsStr;

    if (value != null) {
        dateAsStr = value.toString(format);
    } else {
        dateAsStr = null;
    }

    return dateAsStr;
}

private static DateTime parseDate(String value, DateTimeFormatter format) {

    DateTime strAsDate;

    if (value != null) {
        strAsDate = format.parseDateTime(value);
    } else {
        strAsDate = null;
    }
    return strAsDate;
}
}

答案 1 :(得分:1)

我看到了一些我不认识的.NET Web服务的不直观的实现细节。在我的分析中,我观察到提供完整的日期/时间序列化字符串导致服务器的响应为空,但不是明确的错误。

这是因为在服务器上,它们实际上是使用日期/时间对象,但强制所有时间都在东部标准时间的午夜。因此,如果我将请求中的时间强制到美国东部时间午夜,那么响应只会填充结果,我没有这样做。 (如果没有给出时区,服务器就会假设EST;如果没有给出时间,那么假定是美国东部时间午夜。)

所以在我的情况下,解决方案是修改客户端代码以强制时区为Olson America/New_York并在创建请求时将当地时间强制为00:00:00

答案 2 :(得分:0)

除了Roy的回答,这里是apache cxf和maven cxf codegen配置的有效绑定文件

/resources/binding.jxb

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    jaxb:extensionBindingPrefixes="xjc" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    xmlns="ns" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    jaxb:version="2.1">

    <jaxb:globalBindings>
    <jaxb:serializable />
    <jaxb:javaType name="org.joda.time.DateTime" xmlType="xsd:dateTime"
        printMethod="package.converters.StaticXmlDateConverter.printDateIso"
        parseMethod="package.converters.StaticXmlDateConverter.parseDateIso" />
    <jaxb:javaType name="org.joda.time.DateTime" xmlType="xsd:date"
        printMethod="package.converters.StaticXmlDateConverter.printDateYYYYDashMMDashDD"
        parseMethod="package.converters.StaticXmlDateConverter.parseDateYYYYDashMMDashDD" />
         </jaxb:globalBindings>
</jaxb:bindings>

在pom.xml中

       <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>3.1.6</version>
                <executions>
                    <execution>
                        <id>generate-sources-0001</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <defaultOptions>
                                <frontEnd>jaxws21</frontEnd>
                            </defaultOptions>
                            <sourceRoot>${project.build.directory}/generated/</sourceRoot>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>${basedir}/src/main/resources/service.wsdl</wsdl>
                                    <wsdlLocation>
                                        http://someprovider.com/services/actualservice.asmx?WSDL
                                    </wsdlLocation>
                                    <bindingFiles>
                                        <bindingFile>${basedir}/src/main/resources/binding.xjb</bindingFile>
                                    </bindingFiles>
                                    <extraargs>
                                        <extraarg>-client</extraarg>
                                        <extraarg>-verbose</extraarg>
                                    </extraargs>
                                </wsdlOption>
                            </wsdlOptions>

                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            <dependencies>
                <dependency>
                    <groupId>org.jvnet.jaxb2_commons</groupId>
                    <artifactId>jaxb2-basics-annotate</artifactId>
                    <version>1.0.2</version>
                </dependency>
            </dependencies>
        </plugin>