I have a third party webservice for which I generate a client using wsimport. Each call to the webservice completes successfully, but the response object I get back has all its fields set to null. Monitoring the network I can see that on the wire all of the XML elements in the response message have values in them, so the object should have non-null data in it. Also, a client for the same service generated with old axis1 and called with the same data returns a non-empty response. Any idea what's happening? (In case it makes any difference I'm using MOXy's implementation of JAXB).
Update: I've been able to narrow it down. The wsdl defines object in its own namespace, say http://www.acme.com/ws
. The response I get from the service is
<?xml version="1.0" encoding="UTF-8"?>
... SOAP envelope ...
<ns1:opINFOWLResponse xmlns:ns1="http://www.acme.com/ws"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns1:responseINFOWL xsi:type="ns1:responseINFOWL">
<result>6003</result>
<ndserr/>
<transid>61437594</transid>
<descriptionerr>BLAH.</descriptionerr>
</ns1:responseINFOWL>
</ns1:opINFOWLResponse>
... SOAP closing tags ...
and is unmarshalled to a non null OpINFOWLResponse
which wraps around a non null responseINFOWL
object with all the fields set to null. Just for fun I've tried writing a couple of lines to unmarshal the above snippet (after stripping the SOAP overhead)
JAXBContext ctx = JAXBContext.newInstance(OpINFOWLResponse.class);
Unmarshaller u = ctx.createUnmarshaller();
OpINFOWLResponse o = (OpINFOWLResponse) u.unmarshal(new StringReader(theSnippetAbove));
ResponseINFOWL w = o.getResponseINFOWL();
and I get the same result. If I change the XML above to
<?xml version="1.0" encoding="UTF-8"?>
<ns1:opINFOWLResponse xmlns:ns1="http://www.acme.com/ws"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns1:responseINFOWL xsi:type="ns1:responseINFOWL">
<ns1:result>6003</ns1:result>
<ns1:ndserr/>
<ns1:transid>61437594</ns1:transid>
<ns1:descriptionerr>BLAH.</ns1:descriptionerr>
</ns1:responseINFOWL>
</ns1:opINFOWLResponse>
Everything works fine. Bummer.
Update (again): Same behaviour with both jaxb-RI and Moxy. Still have no idea what's wrong.
Update (Sep. 9): The suggestion below about namespace qualification being wrong is interesting, but I supposed wsimport would get things right. Anyway, this is my package-info.java
@XmlSchema(
namespace = "http://www.acme.com/ws",
elementFormDefault = XmlNsForm.QUALIFIED)
package it.sky.guidaTv.service.remote;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlNsForm;
and this is the relevant part of the ResponseINFOWL
class
/*
* <p>Java class for responseINFOWL complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <complexType name="responseINFOWL">
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="result" type="{http://www.w3.org/2001/XMLSchema}string"/>
* <element name="descriptionerr" type="{http://www.w3.org/2001/XMLSchema}string"/>
* <element name="transid" type="{http://www.w3.org/2001/XMLSchema}string"/>
* <element name="ndserr" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="wallet" type="{http://www.acme.com/ws}t_wallet" minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "responseINFOWL", propOrder = {
"result", "descriptionerr", "transid", "ndserr", "wallet" })
public class ResponseINFOWL {
@XmlElement(required = true)
protected String result;
@XmlElement(required = true)
protected String descriptionerr;
@XmlElement(required = true)
protected String transid;
protected String ndserr;
protected TWallet wallet;
// getters, setters and all.
}
I've tried playing a bit with the namespaces in package-info
but still no joy.
I recently ran into the exact same problem you encountered, and it came down to the fact that the service I was contacting was returning something different from what its WSDL advertised. The service used an old version of Apache Axis (1.4) with behaviour that conflicts with current JAX-WS implementations.
In particular, the namespace on the actual response body contents was NOT what was expected by the client code generated by JAX-WS's wsimport utility. For example, the actual response looked something like this, with the serviceResponse and all its children in namespace "http://foo.com":
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<serviceResponse xmlns="http://foo.com">
<messageReturn>
<messageId>12345</messageId>
<status>Ok</status>
</messageReturn>
</serviceResponse>
</soapenv:Body>
</soapenv:Envelope>
In contrast to what was actually coming back, the client stubs generated by wsimport were expecting something like the response below, with the serviceResponse element in namespace "http://foo.com" and the contained child messageReturn element in the anonymous namespace.
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<n1:serviceResponse xmlns:n1="http://foo.com">
<messageReturn>
<messageId>12345</messageId>
<status>Ok</status>
</messageReturn>
</n1:serviceResponse>
</soapenv:Body>
</soapenv:Envelope>
Since I could not change the service I was consuming, I instead wrote a new WSDL myself that used a wrapped doc-literal binding to explicitly control the expected structure of the response (and request, of course). There is a really good article on WSDL binding types over IBM Developerworks.
The WSDL I created looked something like this:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://foo.com"
xmlns:tns="http://foo.com"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!-- Define the XML types we need to send and receive (used by the message definitions below) -->
<wsdl:types>
<schema targetNamespace="http://foo.com" xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<!-- Reusable types -->
<complexType name="ResponseType">
<sequence>
<element name="messageId" nillable="true" type="xsd:string" />
<element name="status" nillable="true" type="xsd:string" />
</sequence>
</complexType>
<complexType name="InputType">
<sequence>
<element name="firstName" nillable="true" type="xsd:string" />
<element name="lastName" nillable="true" type="xsd:string" />
<element name="command" nillable="true" type="xsd:string" />
</sequence>
</complexType>
<!-- Specific input/output elements used in wsdl:message definitions -->
<element name="serviceResponse">
<complexType>
<sequence>
<element name="messageReturn" type="tns:ResponseType" />
</sequence>
</complexType>
</element>
<element name="serviceRequest">
<complexType>
<sequence>
<element name="message" type="tns:InputType" />
</sequence>
</complexType>
</element>
</schema>
</wsdl:types>
<!-- Define the WSDL messages we send/receive (used by the port definition below) -->
<wsdl:message name="serviceResponseMessage">
<wsdl:part name="part1Name" element="tns:serviceResponse" />
</wsdl:message>
<wsdl:message name="serviceRequestMessage">
<wsdl:part name="part1name" element="tns:serviceRequest" />
</wsdl:message>
<!-- Define the WSDL port (used by the binding definition below) -->
<wsdl:portType name="ServicePort">
<wsdl:operation name="serviceOperation">
<wsdl:input message="tns:serviceRequestMessage" />
<wsdl:output message="tns:serviceResponseMessage" />
</wsdl:operation>
</wsdl:portType>
<!-- Define the WSDL binding of the port (used by the service definition below) -->
<wsdl:binding name="ServiceSoapBinding" type="tns:ServicePort">
<wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="serviceOperation">
<wsdlsoap:operation soapAction="" />
<wsdl:input>
<wsdlsoap:body use="literal" />
</wsdl:input>
<wsdl:output>
<wsdlsoap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<!-- Finally, define the actual WSDL service! -->
<wsdl:service name="UserCommandService">
<wsdl:port binding="tns:ServiceSoapBinding" name="ServicePort">
<!-- This address is just a placeholder, since the actual target URL will be specified at runtime -->
<wsdlsoap:address location="http://localhost:8080/blah" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
With the custom WSDL, I was able to use wsimport to generate client stubs that work perfectly with the service. As well, with the wrapped doc-literal approach, I completely control the expected structure and namespace of the request/response, so I can implement multiple namespaces in that XML if necessary.
Enjoy...