I've been wanting to ask this question of the list for some time, but I've put
it trying to see how far I could get.
Firstly, I am using a code first method and generating the WSDL. CXF 2.3.1,
JBoss6. No options to upgrade further.
I have written an application level framework that makes it very easy for me to
implement new web services and operations, which usually are just exposing some
information from a database. This framework takes care of auditing records of
web service requests, supports requests of asynchronous data extracts,
polling the status of the extract, and returning results, optionally in
"chunks".
In addition to the results, I wanted all of my web service responses to include
some metadata about that request/response. So, I created a "PayLoad" object
which would contain a ResponseInfo object with the metadata and a
List<PayLoadOjbect>. So, all of my operations, across all web services return
a PayLoad with its PayLoadList containing the results of their request.
Now, if you have an opinion on the general design of return results in an
enclosing object with metadata like this, whether or not there are other,
better patterns of doing this or difficulties of consuming such services as a
client, I'd be interested to hear them. Otherwise...
...my problem is the list of results. For some time I have had it returning
<PayLoad>
<responseInfo> ...</responseInfo>
<payLoadList>
<payLoadObject xsi:type="ns2:MyBean"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...
</payLoadObject>
...
</payLoadList>
</PayLoad>
My problem with this is that, even though all of the complex types returned are
defined in the WSDL (because I have @XmlSeeAlso annotations in the service),
there was no way for a consumer of the WSDL to determine exactly what type is
returned by a specific operation. Clients accessing the PayLoad list would
need to "know" what type to expect in the payLoadList and cast them to the
proper type. (Clients have to have certs on file, so this seemed like a
documentation problem to me.) Also, I didn't like having the abstract super
class, because if I needed to write a web service operation "aligned" with some
external third party webservice, i.e., returned objects from other namespaces,
I would have to modify them to extend that class to work with my framework.
So, next I got rid of PayLoadObject abstract class and just had my PayLoadList
returning a list of Object. The response XML looks exactly the same. (I had
not read about XML substitution groups, which sound promising and seem to
require an abstract super class , but the aforementioned negatives still would
be a consideration. Is this the only way I can accomplish what I want and have
it validate??)
Still, what I wanted was for the PayLoadList to look like *this*, with nodes of
the proper element name appearing in the payLoadList, not objects with a type
attribute. I am still quite a novice at writing web service clients, but in my
first attempt, I had to cast the objects in the payLoadList to appropriate
objects. JAXB didn't unmarshal objects of the actual result type.
<payLoadList>
< ns2:MyBean>...</ns2:MyBean>
...
</payLoadObject>
The next thing I tried was parameterizing my PayLoad class, so that I would
have my framework work with PayLoad<T>, my web service operations return
PayLoad<ResultObjectClass>. But the result was still the same, and not what
I wanted.
So, along the lines of this blog tutorial,
http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html, I
created a ListWrapper<T> object, modified the List<T> in my PayLoad<T> to be a
ListWrapper<T>.
In the ListWrapper<T>, the get method for the enclosed list is annotated with
@XmlAnyElement(lax=true)
Now, the XML being returned is exactly the way I want it! But, my NEW problem
is that my WSDL still doesn't seem to adequately describe what is being
returned. So, now my SoapUi test cases are all failing schema compliance
assertions with an error along the lines of "Element not allowed: MyBean in
element payLoadList".
So, in my WSDL:
<xs:complexType name="payLoad">
<xs:sequence>
<xs:element minOccurs="0" name="responseInfo" type="tns:wsResponseInfo" />
<xs:element minOccurs="0" name="payLoadList" type="tns:listWrapper" />
</xs:sequence>
</xs:complexType>
...
<xs:complexType name="listWrapper">
<xs:sequence>
<xs:any maxOccurs="unbounded" minOccurs="0" namespace="##other"
processContents="lax" />
</xs:sequence>
</xs:complexType>
I don't know how to set namespace="##targetNamespace". Would that solve my
problem? Is the substitution group approach
(like this:
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html)
the only way (I'd have to resurrect my PayLoadObject)? I'm not sure I
understand this completely yet, but I'd like to avoid any dependencies of my
framework classes, like PayLoad, on result classes defined in web service
client modules.
Any suggestions or thoughts?
n Andrew