XMLGregorianCalendar 日期序列化为空字符串

发布于 2024-12-20 13:32:47 字数 2707 浏览 3 评论 0原文

我使用 Java 1.6 wsimport 从 Web 服务的 WSDL 生成源代码。请求结构中的字段之一在 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。使用调试器检查日期对象本身,我看到它的年、日和月字段都已填充,所有其他字段都是null-2147483648,这是 DatatypeConstants.FIELD_UNDEFINED 的值。根据我找到的所有文档和互联网搜索结果,我的日期对象的格式正确。

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

Using Java 1.6 wsimport I generated source from a WSDL for a web service. One of the fields in the request structure has type xs:dateTime in the XML schema included by the WSDL and type javax.xml.datatype.XMLGregorianCalendar in the generated code.

Through manual testing with soapUI I have determined that the following serialized values are accepted by the web service: 2011-12-08, 2011-12-08Z. The following are not accepted and the response in this case is an empty reply (not an explicit error): 2011-12-08T20:00:00, 2011-12-08T20:00:00-05:00. The service itself is .NET powered if that matters.

My thought is that the server should accept the full date/time and reject the date only, but the other way around is what is happening. But I am under no assumption that maintainers of the server will be open to change. So I have attempted to convince the client to send a date only.

I can't convince my client code to serialize an XMLGregorianCalendar object into a date only. Well actually I can, except when the generated code does it. When the generated client code (produced by wsimport) does it, the serialized value is the empty string, and the server correctly returns an error. I verified this using a packet sniffer.

Here is how I'm creating and populating the date field in the request:

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

The implementation class for XMLGregorianCalendar in my case is com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl. In the debugger, or if I add log output, calling the date object's toXMLFormat() method returns a date only, such as 2011-12-09. Using a debugger to inspect the date object itself, I see that its year, day and month fields are populated, and all the others are either null or -2147483648 which is the value of DatatypeConstants.FIELD_UNDEFINED. According to all documentation and Internet search results I have found, my date object is correctly formed.

Am I crazy? Is the server really in error? Is the refusal of the generated client code to send a date only correct? Is this a justifiable "undefined behavior" case? Is the wrong implementation class being used (could that possibly matter anyway)? Is there some known issue with wsimport that's affecting me?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

向日葵 2024-12-27 13:32:47

我发现 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;
}
}

I've found that JAXB's date conversions should not be left to their own devices. Not as familiar with wsimport, but does it allow you specify a binding file? I use Joda Date/Time but the idea is the same I'm sure. Added this to my 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>

With a class as such:

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;
}
}
昇り龍 2024-12-27 13:32:47

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

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

因此,就我而言,解决方案是修改客户端代码以强制时区为Olson America/New_York 并在创建请求时强制将当地时间设置为 00:00:00

I was seeing an unintuitive implementation detail of .NET web services that I did not recognize. In my analysis I observed that providing a complete date/time serialized string resulted in an empty response from the server, but not an explicit error.

This was because on the server, they were in fact using date/time objects, but were forcing all of the times to midnight Eastern Standard Time. So the responses were only populated with results if I forced the time in the request to midnight EST, which I was not doing. (In the event that no time zone was given, the server assumed EST; and if no time was given at all, midnight EST was assumed.)

So in my case, the solution was to modify the client code to force the time zone to Olson America/New_York and force the local time to 00:00:00 when creating the request.

回忆追雨的时光 2024-12-27 13:32:47

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

In addition to Roy's answer here is valid binding file for apache cxf with maven cxf codegen configuration

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

inside 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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文