Ok, let me try and demonstrate this a bit more. With the following example
object model:
-----------------------------------------------
abstract class Animal
{
String colour;
}
class Bird extends Animal
{
boolean canFly;
}
class Cat extends Animal
{
String breed;
}
class MyAnimals
{
// My cats have names, so the map is name -> cat
Map<String,Cat> cats;
List<Bird> birds;
}
-----------------------------------------------
If I have a method that returns a MyAnimals object the following types are
in the
default WSDL provided by Axis.
-----------------------------------------------
<types>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://xml.apache.org/xml-soap">
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
<complexType name="mapItem">
<sequence>
<element name="key" nillable="true" type="xsd:string"/>
<element name="value" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<complexType name="Map">
<sequence>
<element name="item" minOccurs="0" maxOccurs="unbounded"
type="apachesoap:mapItem"/>
</sequence>
</complexType>
</schema>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://domain/">
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
<complexType name="MyAnimals">
<sequence>
<element name="birds" nillable="true" type="soapenc:Array"/>
<element name="cats" nillable="true" type="apachesoap:Map"/>
</sequence>
</complexType>
<complexType abstract="true" name="Animal">
<sequence>
<element name="colour" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<complexType name="Bird">
<complexContent>
<extension base="tns1:Animal">
<sequence>
<element name="canFly" type="xsd:boolean"/>
</sequence>
</extension>
</complexContent>
</complexType>
<complexType name="Cat">
<complexContent>
<extension base="tns1:Animal">
<sequence>
<element name="breed" nillable="true"
type="xsd:string"/>
</sequence>
</extension>
</complexContent>
</complexType>
</schema>
</types>
-----------------------------------------------
As you can see the MyAnimals type is described as a Map of string to string
and a soapenc:Array of unknown type, which I believe is automagically using
the ArraySerializer for the List.
Now if I try to use the .Net wsdl.exe proxy generator from this I get:
Error: Unable to import binding 'MyServiceSoapBinding' from namespace
'http://localhost:8084/services/MyService'.
- Unable to import operation 'getAnimals'.
- The datatype 'Array' is missing.
So obviously I cannot simply generate a .Net client from the WSDL.
And even if I could, the fact that the Map is actually a map of string to
Cat rather than a Map of string to string, will surely cause some serious
problems down the track.
However if I changed the MyAnimals class to be like this:
-----------------------------------------------
class MyAnimals
{
StringCatMapElement[] cats;
Bird[] birds;
}
class StringCatMapElement
{
String key;
Cat value;
}
-----------------------------------------------
the types in the wsdl then become
-----------------------------------------------
<types>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://domain/">
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
<complexType abstract="true" name="Animal">
<sequence>
<element name="colour" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<complexType name="Bird">
<complexContent>
<extension base="tns1:Animal">
<sequence>
<element name="canFly" type="xsd:boolean"/>
</sequence>
</extension>
</complexContent>
</complexType>
<complexType name="Cat">
<complexContent>
<extension base="tns1:Animal">
<sequence>
<element name="breed" nillable="true"
type="xsd:string"/>
</sequence>
</extension>
</complexContent>
</complexType>
<complexType name="StringCatMapElement">
<sequence>
<element name="key" nillable="true" type="xsd:string"/>
<element name="value" nillable="true" type="tns1:Cat"/>
</sequence>
</complexType>
<complexType name="MyAnimals">
<sequence>
<element name="birds" nillable="true"
type="impl:ArrayOf_tns1_Bird"/>
<element name="cats" nillable="true"
type="impl:ArrayOf_tns1_StringCatMapElement"/>
</sequence>
</complexType>
</schema>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://localhost:8084/services/MyService">
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
<complexType name="ArrayOf_tns1_Bird">
<complexContent>
<restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType"
wsdl:arrayType="tns1:Bird[]"/>
</restriction>
</complexContent>
</complexType>
<complexType name="ArrayOf_tns1_StringCatMapElement">
<complexContent>
<restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType"
wsdl:arrayType="tns1:StringCatMapElement[]"/>
</restriction>
</complexContent>
</complexType>
</schema>
</types>
-----------------------------------------------
As you can see with this, the types are all relatively simple in SOAP terms,
as they all boil down to arrays, strings or booleans.
And the .Net wsdl proxy generator can generate a proxy and all the necessary
types (Cat, Bird, Animal, MyAnimals, StringCatMapElement) without ANY
intervention on my behalf.
This is ideally the point I want to get to, whereby a client can take the
wsdl and generate all the necessary boilerplate code needed to use the
WebService.
Now what I want to be able to get Axis to treat
class MyAnimals
{
Map<String,Cat> cats;
List<Bird> birds;
}
as
class MyAnimals
{
StringCatMapElement[] cats;
Bird[] birds;
}
when it is generating the WSDL and serialising/deserialising the data, so
that I don't have to change the existing complex object model to provide it
as a Soap friendly data structure.
*
* I am going to attempt to write a (de)serialiser to do this, and if anyone
has any ideas I would appreciate them.
*
In terms of interoperability with different clients, .Net 1.1 clients are my
main focus at the moment, however inter-operability with other clients, be
they php, perl, VB or whatever, is still important.
*
* Anand, can you expand on any interoperability issues you see might appear
* when writing/generating a client for the types in the second WSDL.
*
Cheers
-Marc
>From: Anand Natrajan <an4m@cs.virginia.edu>
>Reply-To: Anand Natrajan <anand@anandnatrajan.com>
>To: axis-user@ws.apache.org
>Subject: RE: Advanced serialization of Collections and Maps
>Date: Wed, 28 Jul 2004 12:59:54 -0400 (EDT)
>MIME-Version: 1.0
>Received: from mail.apache.org ([209.237.227.199]) by mc1-f38.hotmail.com
>with Microsoft SMTPSVC(5.0.2195.6824); Wed, 28 Jul 2004 10:00:15 -0700
>Received: (qmail 14849 invoked by uid 500); 28 Jul 2004 17:00:02 -0000
>Received: (qmail 14687 invoked by uid 99); 28 Jul 2004 17:00:01 -0000
>Received: from [128.143.137.19] (HELO ares.cs.Virginia.EDU)
>(128.143.137.19) by apache.org (qpsmtpd/0.27.1) with ESMTP; Wed, 28 Jul
>2004 09:59:58 -0700
>Received: from viper.cs.Virginia.EDU (viper.cs.Virginia.EDU
>[128.143.137.17])by ares.cs.Virginia.EDU (8.12.10/8.12.10/UVACS-2003031900)
>with ESMTP id i6SGxsYG014840for <axis-user@ws.apache.org>; Wed, 28 Jul 2004
>12:59:55 -0400 (EDT)
>Received: from localhost (an4m@localhost)by viper.cs.Virginia.EDU
>(8.12.10+Sun/8.12.10/Submit) with ESMTP id i6SGxs4s018240for
><axis-user@ws.apache.org>; Wed, 28 Jul 2004 12:59:54 -0400 (EDT)
>X-Message-Info: JGTYoYF78jFQdE4MZk4cwFHXZMygq++k
>Mailing-List: contact axis-user-help@ws.apache.org; run by ezmlm
>Precedence: bulk
>list-help: <mailto:axis-user-help@ws.apache.org>
>list-unsubscribe: <mailto:axis-user-unsubscribe@ws.apache.org>
>list-post: <mailto:axis-user@xml.apache.org>
>Delivered-To: mailing list axis-user@ws.apache.org
>X-ASF-Spam-Status: No, hits=0.0 required=10.0tests=CLICK_BELOW
>X-Spam-Check-By: apache.org
>In-Reply-To:
><C8646A111690BC44862C29AFFF5CDCCE28D74E@GHX-EXCHANGE.corp.ghx.com>
>Message-ID: <Pine.GSO.4.58.0407281255560.17050@viper.cs.Virginia.EDU>
>References:
><C8646A111690BC44862C29AFFF5CDCCE28D74E@GHX-EXCHANGE.corp.ghx.com>
>X-Virus-Checked: Checked
>Return-Path: axis-user-return-25849-scorchers=hotmail.com@ws.apache.org
>X-OriginalArrivalTime: 28 Jul 2004 17:00:15.0273 (UTC)
>FILETIME=[5A6D2190:01C474C4]
>
>Marc and Brian,
>
>Forgive me if this is a naive question, but won't your actions below make
>it
>hard to write non-Java clients targetting your web services? It may be hard
>for Perl and VB clients to duplicate the complex data structures you're
>planning, even if you manage to make Java clients work. Moreover, if you
>write custom deserialisers, won't all your clients have to have them as
>well? That means you can't just hand a client a WSDL - you have to give
>them
>a deserialiser as well. On the other hand, if you can flatten your complex
>types into arrays or simple types, you're guaranteed that non-Java clients
>can deal with them.
>
>I may be wrong about my concerns above, in which case I'd like to know how
>you go about resolving them. I am interested in the solution because I may
>face a similar decision in the near future. Thanks!
>
>Anand
>
>On Wed, 28 Jul 2004, Brian Drake wrote:
>
>: Hi Marc --
>:
>: If I understand the first part of your question correctly, you want to
>: have your service method return the data in a Collection object back to
>: the client as a typed array.
>:
>: There is an array serializer/deserializer that will work fine for this
>: purpose. They're in the "ser" package along with the Bean serializers
>: and such.
>:
>: I've been interested in doing the same sort of stuff. Sending back
>: typed arrays is about as close to returning a Collections object as
>: I've been able to get. For the most part this works for my (current)
>: needs. It doesn't require custom serialization (unless the array
>: type does).
>:
>: -BWD.
>:
>: -----Original Message-----
>: From: Marc Wilson [mailto:
>: Sent: Tuesday, July 27, 2004 8:46 PM
>: To: axis-user@ws.apache.org
>: Subject: Advanced serialization of Collections and Maps
>:
>:
>: Hi,
>:
>: I have a few questions about serialization and deserialization of
>: Collections and Maps with Axis.
>:
>: Basically our app has a Business Object Model of DTOs (Data Transfer
>: Objects) that it relatively complex from a Soap p.o.v., ie: it contains
>: lists, sets and maps within it. All the lists, sets and maps in the BOM
>: contain elements of one type only.
>:
>: We are evaluating the practicallity of exposing the API of our
>application
>: (currently accessed through Stateless Session EJBs) also as a WebService
>: through AXIS.
>:
>: I have got it working for "simple" return values, but for complex return
>: values as I expected it doesn't work as easily.
>:
>: What I want to be able to do is use the serialization and deserialization
>: abilities of Axis to serialise any and all Collections in the return
>values
>: as typed arrays (I'll get to maps in a minute).
>:
>: I think that the BeanSerializer can currently serialize a List as
>: xsd:anyType[] or soapenc:Array, but I need it to be typed when it is
>: serialized so that any client can generate everything it needs from the
>wsdl
>: and not have to worry about any complex mappings etc..
>:
>: I plan to markup the DTOs and use xDoclet and to generate custom
>: serialization and deserialization classes:
>: ie:
>: ----------------------------------------
>: class MouseDTO
>: {
>: /**
>: * @mymarkuptag.list type=Cat
>: */
>: public List getCats() ...
>:
>: /**
>: * @mymarkuptag.set type=Dog
>: */
>: public Set getDogs() ...
>: }
>: class CatDTO
>: {
>: ..
>: }
>: class DogDTO
>: {
>: ..
>: }
>: ----------------------------------------
>: would generate
>: ----------------------------------------
>: class FooDTOSerialiser extends MyCollectionSerializer
>: {
>: FooDTOSerialiser()
>: {
>: super(new String[]{"cats","dogs"}, new Class[]{CatDTO.class,
>: DogDTO.class});
>: }
>: }
>: ----------------------------------------
>:
>: Then I plan to write a MyCollectionSerializer base class which will act
>like
>: the current BeanSerializer (probably will extend it) but for the
>properties
>: that were specified in the constructor (ie: cats) it will do the
>: serialisation as if it were an array of that type (ie: CatDTO[]).
>:
>: Maps would work the same way except that there would be two classes
>marked
>: up for the key and value types and they would be serialised as an array
>of a
>: type that contains the key and value types (ie: TypeATypeBPair[], where
>: TypeATypeBPair is { TypeA key, TypeB value}).
>:
>: Now, the questions:
>:
>: Does what I want to do make sense?
>:
>: Is there anything out in Axis land that does this already?
>:
>: Does anyone have any ideas/input into how I would/should write the base
>: serializer classes?
>:
>: Any other ideas of feedback would be appreciated
>:
>: Cheers
>: -Marc
>:
>: _________________________________________________________________
>: SEEK: Now with over 50,000 dream jobs! Click here:
>: http://ninemsn.seek.com.au?hotmail
>:
>:
>:
_________________________________________________________________
Feeling spent? Apply here for emergency plastic surgery:
http://ad.doubleclick.net/clk;9577691;9687279;r?http://www.promo.com.au/virgincreditcard/plasticsurgery/track.cfm?source=P08