Why does wsimport have trouble with server object

2019-04-16 15:58发布

问题:

I was completing work on a web service using JAX-WS on the server side. In many of the domain objects I used @XmlRootElement to help facilitate the unmarshaling of XML files into the service using JAXB. All went well and the output was what I expected to see using SoapUI.

However, when I used wsimport to create the client (as a convenience DAO for other developers), I started encountering NullPointerExceptions in my client integration-test class.

The call to the webservice worked correctly, and a response was received by the client, but my more-complex objects were null. Simple attributes, like Strings, were returning full of usable data, but not the larger objects.

Through iterations of recreating services using simple Strings and migrating to more complex objects, I discovered that when the client received objects that were declared on the server with @XmlRootElement, these were the objects that were null. If the server object did not have the @XmlRootElement annotation, the client received all of the data in all of its complex glory.

Initially the lack of @XmlRootElement gave me fits with unmarshaling the data on the server, but this answer helped me out.

So, the phenomenon of the wsimport client silently failing on the unmarshaling of the web service response because of the @XmlRootElement annotation (on the server!) has me concerned. In this case I had control of both sides and could do something about it. But, what if I don't have control of the server? How would I have resolved this with just the wsimport-generated code?

回答1:

Found the answer, or the reason, so thought I would share.

The @XmlRootElement annotation is useful for plain JAXB bindings, but when the objects (and the resulting XML) are packaged as a SOAP response it is possible that they don't exactly match the WSDL's representation of the data depending on the value of other annotations.

With the @XmlRootElement annotation on the class on the server which is being returned by a @WebMethod method, the WSDL will include an element definition such as:

<xs:element name="foo" type="tns:FooType"/>

then elsewhere your WSDL will include a reference to the element in a sequence such as:

<xs:seqeunce>
<xs:element maxOccurs="unbounded" minOccurs="0" ref="tns:foo"/>
</xs:sequence>

This referencing is caused by the @XmlRootElement annotation may confuse the intent of the root element declaration compared to the actual XML of the SOAP response.

In contrast, the WSDL generated without the @XmlRootElement annotation on the server objects does not contain a <xs:element name="foo"/> declaration at all. Rather its element is described as:

<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="foo" type="tns:FooType"/>
</xs:sequence>

This probably better matches the way the SOAP response XML is represented and the unmarshalling of the XML into the classes generated by wsimport works just fine.

How to use @XmlRootElement in a JAX-WS service?

wsimport seems to handle some degree of laziness in the validity of the XML returned by a service. The lesson learned is to be diligent in your use of name and targetNamespace on your @WebResult annotations describing your web service method. The @XmlRootElement annotation needs to match the name within the targetNamespace. When they all match, the unmarshalling happens as expected. When those values do not match, your stubbed classes generated and annotated by wsimport will not be able to properly consume the XML.