XMLGregorianCalendar дата сериализации в пустую строку - PullRequest
3 голосов
/ 09 декабря 2011

Использование Java 1.6 wsimport Я сгенерировал исходный код из WSDL для веб-службы. Одно из полей в структуре запроса имеет тип xs:dateTime в схеме XML, включенной в WSDL, и тип javax.xml.datatype.XMLGregorianCalendar в сгенерированном коде.

Путем ручного тестирования с помощью soapUI я определил, что веб-сервисом принимаются следующие сериализованные значения: 2011-12-08, 2011-12-08Z. Следующее не принимается, и ответ в этом случае является пустым ответом (не явной ошибкой): 2011-12-08T20:00:00, 2011-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. Используя отладчик для проверки самого объекта даты, я вижу, что его поля year, day и month заполнены, а все остальные - либо null, либо -2147483648, что является значением DatatypeConstants.FIELD_UNDEFINED , Согласно всей найденной мной документации и результатам поиска в Интернете, мой объект даты сформирован правильно.

Я сумасшедший? Сервер действительно в ошибке? Является ли отказ от сгенерированного клиентского кода отправкой даты только правильным? Это оправданный случай "неопределенного поведения"? Используется ли неправильный класс реализации (может ли это иметь значение в любом случае)? Есть ли какая-то известная проблема с wsimport, которая затрагивает меня?

Ответы [ 3 ]

3 голосов
/ 09 декабря 2011

Я обнаружил, что преобразования даты в JAXB не следует оставлять на их собственные устройства. Не так хорошо знаком с wsimport, но позволяет ли он указывать файл привязки? Я использую Joda Date / Time, но идея та же самая, я уверен. Добавил это в мой 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 голос
/ 09 декабря 2011

Я видел неинтуитивную деталь реализации веб-сервисов .NET, которую я не узнал. В своем анализе я заметил, что предоставление полной сериализованной строки даты / времени привело к пустому ответу от сервера, но не явной ошибке.

Это потому, что на сервере они фактически использовали объекты даты / времени, но все время переходили в полночь по восточному поясному времени. Таким образом, ответы были заполнены только результатами, если я перевел время запроса в полночь по EST, чего я не делал. (В случае, если часовой пояс не был указан, сервер принимал EST; а если время вообще не указывалось, предполагалось полночное EST.)

Таким образом, в моем случае решение состояло в том, чтобы изменить код клиента, чтобы заставить часовой пояс Olson America/New_York и принудительно установить местное время на 00:00:00 при создании запроса.

0 голосов
/ 24 декабря 2016

В дополнение к ответу Роя, здесь есть действующий файл привязки для apache cxf с конфигурацией codegen maven cxf

/ ресурсы / 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>
...