5 Stimmen

XMLGregorianCalendar Datum serialisiert zu leerem String

Java 1.6 verwenden wsimport Ich habe den Quellcode aus einer WSDL für einen Webdienst generiert. Eines der Felder in der Anforderungsstruktur hat den Typ xs:dateTime im XML-Schema, das die WSDL enthält, und Typ javax.xml.datatype.XMLGregorianCalendar im generierten Code.

Durch manuelle Tests mit soapUI habe ich festgestellt, dass die folgenden serialisierten Werte vom Webdienst akzeptiert werden: 2011-12-08 , 2011-12-08Z . Die folgenden Angaben werden nicht akzeptiert und die Antwort ist in diesem Fall eine leere Antwort (kein expliziter Fehler): 2011-12-08T20:00:00 , 2011-12-08T20:00:00-05:00 . Der Dienst selbst ist .NET-basiert, falls das eine Rolle spielt.

Mein Gedanke ist, dass der Server das vollständige Datum und die Uhrzeit akzeptieren und nur das Datum zurückweisen sollte, aber genau das Gegenteil ist der Fall. Aber ich gehe nicht davon aus, dass die Betreiber des Servers offen für Änderungen sind. Also habe ich versucht, den Client davon zu überzeugen, nur ein Datum zu senden.

Ich kann meinen Client-Code nicht dazu bringen, eine XMLGregorianCalendar Objekt nur in ein Datum umwandeln. Nun, eigentlich kann ich das, außer wenn der generierte Code dies tut. Wenn der generierte Client-Code (erzeugt von wsimport ), ist der serialisierte Wert eine leere Zeichenkette, und der Server gibt korrekt einen Fehler zurück. Ich habe dies mit einem Packet Sniffer überprüft.

Im Folgenden wird beschrieben, wie ich das Datumsfeld in der Anfrage erstelle und auffülle:

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

Die Implementierungsklasse für XMLGregorianCalendar lautet in meinem Fall com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl . Im Debugger, oder wenn ich eine Log-Ausgabe hinzufüge, wird der Aufruf des Datumsobjekts toXMLFormat() Methode gibt nur ein Datum zurück, z. B. 2011-12-09 . Wenn ich einen Debugger verwende, um das Datumsobjekt selbst zu untersuchen, sehe ich, dass sein year , day y month Felder sind ausgefüllt, und alle anderen sind entweder null o -2147483648 das ist der Wert von DatatypeConstants.FIELD_UNDEFINED . Nach allen Unterlagen und Internet-Suchergebnissen, die ich gefunden habe, ist mein Datumsobjekt korrekt geformt.

Bin ich verrückt? Ist der Server wirklich im Irrtum? Ist die Weigerung des generierten Client-Codes, nur ein Datum zu senden, korrekt? Ist dies ein vertretbarer Fall von "undefiniertem Verhalten"? Wird die falsche Implementierungsklasse verwendet (könnte das überhaupt eine Rolle spielen)? Gibt es ein bekanntes Problem mit wsimport die sich auf mich auswirkt?

4voto

Roy Truelove Punkte 21396

Ich habe festgestellt, dass die Datumsumwandlungen von JAXB nicht sich selbst überlassen werden sollten. Ich bin mit wsimport nicht so vertraut, aber können Sie damit eine Bindungsdatei angeben? Ich verwende Joda Date/Time, aber die Idee ist die gleiche, ich bin sicher. Ich habe dies zu meiner binding.xjb hinzugefügt:

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

Mit einer Klasse als solcher:

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

1voto

wberry Punkte 17277

Ich habe ein unintuitives Implementierungsdetail von .NET-Webdiensten gesehen, das ich nicht kannte. Bei meiner Analyse stellte ich fest, dass die Bereitstellung einer vollständigen serialisierten Zeichenfolge für Datum und Uhrzeit zu einer leeren Antwort des Servers führte, aber nicht zu einem expliziten Fehler.

Dies lag daran, dass auf dem Server zwar Datums-/Zeitobjekte verwendet wurden, aber alle Zeiten auf Mitternacht Eastern Standard Time gesetzt wurden. Die Antworten enthielten also nur dann Ergebnisse, wenn ich die Zeit in der Anfrage auf Mitternacht EST setzte, was ich nicht tat. (Wurde keine Zeitzone angegeben, ging der Server von EST aus; wurde überhaupt keine Zeit angegeben, wurde Mitternacht EST angenommen).

In meinem Fall bestand die Lösung also darin, den Client-Code so zu ändern, dass die Zeitzone auf Olson America/New_York und die Ortszeit auf 00:00:00 bei der Erstellung der Anfrage.

0voto

Boris Treukhov Punkte 16973

Zusätzlich zu Roys Antwort ist hier eine gültige Bindungsdatei für apache cxf mit maven cxf codegen Konfiguration

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

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X