Why do people like to use xsd:any? I hear many reasons: for loose coupling, to enable versioning, for completely flexible polymorphism. All of these reasons come down to not changing the API. That's a noble reason, but in practice, using xsd:any is neither noble nor practical. If you're working in a dynamic environment, purely with XML, then xsd:any may, indeed, be appropriate. But most people do not like the complexity of dynamic access to XML (in other words, the SAX or DOM or SAAJ programming models), and even when they do, there are gradations of dynamicity. And XML rarely exists in a vaccuum. XML almost always maps to or interacts with something else. For instance, an XML schema might exist as part of a Web service, which means that you will likely deal with a language binding. So you should be aware of how that language binding maps xsd:any to the given language. In this tip, I focus on the JAX-RPC Java™ language binding.
An example Web service using xsd:any
Let's look at the example WSDL file in Listing 1. It is a simple veterinary clinic Web service. It defines an operation called registerPet, whose input message contains an xsd:any. This WSDL also defines a couple of pet types: cat and fish.
Listing 1. Veterinarian service WSDL using
xsd:any
<?xml version="1.0" encoding="UTF-8"?>
<definitions
targetNamespace="urn:any"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="urn:any"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<types>
<schema
targetNamespace="urn:any"
xmlns:tns="urn:any"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.w3.org/2001/XMLSchema">
<complexType name="Cat">
<sequence>
<element name="name" type="xsd:string"/>
<element name="breed" type="xsd:string"/>
</sequence>
</complexType>
<complexType name="Fish">
<sequence>
<element name="name" type="xsd:string"/>
<element name="saltWater" type="xsd:boolean"/>
</sequence>
</complexType>
<element name="registerPet">
<complexType>
<sequence>
<any namespace="##any"/>
</sequence>
</complexType>
</element>
<element name="registerPetResponse">
<complexType>
<sequence/>
</complexType>
</element>
</schema>
</types>
<message name="registerPetRequest">
<part name="registerPetRequest" element="tns:registerPet"/>
</message>
<message name="registerPetResponse">
<part name="registerPetResponse" element="tns:registerPetResponse"/>
</message>
<portType name="Vet">
<operation name="registerPet">
<input message="tns:registerPetRequest"/>
<output message="tns:registerPetResponse"/>
</operation>
</portType>
<binding name="VetSOAP" type="tns:Vet">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="registerPet">
<soap:operation/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="VetService">
<port name="VetSOAP" binding="tns:VetSOAP">
<soap:address location="http://localhost:9080/VetService/services/Vet"/>
</port>
</service>
</definitions>
|
The xsd:any parameter to registerPet can be either of the following:
- An instance of a
Cator aFish - An instance of some other pet type that may be defined elsewhere or at another time.
xsd:any deviates from the intent of WSDL
Notice that the two bullet points above are not defined in the WSDL. I made them up. WSDL is the Web Services Description Language. Its charter is to describe an interface to a service as completely as possible. When you use xsd:any, you deviate from this intent of WSDL.
The first bullet states that the parameter is going to be a cat or a fish. But the WSDL doesn't tell you that. All it says is that the parameter can be anything. Given that this is a veterinarian service, it likely won't handle orders or insurance claims or tax statements -- but you're just guessing; the WSDL doesn't tell you that.
A better approach to this pseudo-polymorphism would be to define true polymorphism. Define a base type, called Pet, and rewrite Cat and Fish to extend Pet.
The second bullet states that other types will be defined elsewhere or at another time. That tells me that the WSDL isn't the complete interface for this service. It may appear that you can change the service and keep the API unchanged, but since this WSDL isn't the complete API, that's a false statement. For a client using this WSDL to communicate successfully to this service, the WSDL is not enough. Can a client send an alligator to the service? Just by looking at the WSDL, you don't know (I don't know too many vets who would support an Alligator type!). You need some additional, out-of-band definitions.
JAX-RPC is not particularly friendly with xsd:any
Finally, in order to use this service, you have to map this WSDL to some language binding. I'll pick on the JAX-RPC specification. According to JAX-RPC, xsd:any maps to javax.xml.soap.SOAPElement, a type defined in the SAAJ specification. SAAJ is a DOM-like API used to access XML instances in a SOAP message. SAAJ is a very dynamic, very low-level, and not particularly friendly means of accessing XML instance data. See Listing 2 for the Java interface for the WSDL in Listing 1.
Listing 2. Java interface for WSDL using
xsd:any
package any;
public interface Vet extends java.rmi.Remote {
public void registerPet(javax.xml.soap.SOAPElement any)
throws java.rmi.RemoteException;
} |
(For a discussion of a SAAJ implementation, see the Resources for a link to "how to use the SAAJ API".)
This may be a very good mapping for a dynamic programmer, but the rest of us would rather deal with the generated classes Cat and Fish rather than with SOAPElement. It would be nice if you could just push a Cat or Fish into a SOAPElement, but there is no SAAJ-specified way to do so. JAX-RPC dictates that xsd:any maps to SOAPElement, yet it fails to provide a means by which you can transform instances of the classes into or from SOAPElement.
If you are comfortable working with DOM, SAX, or SAAJ, then xsd:any will be no problem for you. However, keep in mind the users of your WSDL. Just because you are comfortable with this level of programming, will the other users of your WSDL be comfortable with it as well?
An alternative to xsd:any is xsd:anyType (see Listing 3 for the WSDL -- the change from Listing 1 is highlighted in bold -- and Listing 4 for the Java interface). The disadvantage of xsd:anyType is that JAX-RPC does not define a mapping for it. So even if a vendor maps this XML type, you cannot write portable code which uses it. With that said, however, an advantage to this type is that some vendors, like IBM, map it to a somewhat useful type, like java.lang.Object. In the case of WebSphere Application Server's SOAP engine, if the method takes a parameter of type java.lang.Object, then you can send any Java object to that method and, as long as it is an XML-compliant object, it will then be serialized into the SOAP message. Conversely, on the receiving side, if the SOAP engine knows how to deserialize the XML instance into a Java object, it will do so. If the engine does not know how to deserialize the object, the receiver will get a SOAPElement, just as it would for xsd:any.
Keep in mind that, even though xsd:anyType may seem better than xsd:any, it is not a supported JAX-RPC mapping. So it is only better if:
- You don't care that your code is not portable.
- The underlying platform for the implementations of both the service and the clients to the service have some mapping for
xsd:anyType.
Listing 3. Veterinarian service WSDL using
xsd:anyType
<?xml version="1.0" encoding="UTF-8"?>
<definitions
targetNamespace="urn:any"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="urn:any"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<types>
<schema
targetNamespace="urn:any"
xmlns:tns="urn:any"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.w3.org/2001/XMLSchema">
<complexType name="Cat">
<sequence>
<element name="name" type="xsd:string"/>
<element name="breed" type="xsd:string"/>
</sequence>
</complexType>
<complexType name="Fish">
<sequence>
<element name="name" type="xsd:string"/>
<element name="saltWater" type="xsd:boolean"/>
</sequence>
</complexType>
<element name="registerPet">
<complexType>
<sequence>
<element name="pet" type="xsd:anyType"/>
</sequence>
</complexType>
</element>
<element name="registerPetResponse">
<complexType>
<sequence/>
</complexType>
</element>
</schema>
</types>
<message name="registerPetRequest">
<part name="registerPetRequest" element="tns:registerPet"/>
</message>
<message name="registerPetResponse">
<part name="registerPetResponse" element="tns:registerPetResponse"/>
</message>
<portType name="Vet">
<operation name="registerPet">
<input message="tns:registerPetRequest"/>
<output message="tns:registerPetResponse"/>
</operation>
</portType>
<binding name="VetSOAP" type="tns:Vet">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="registerPet">
<soap:operation/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="VetService">
<port name="VetSOAP" binding="tns:VetSOAP">
<soap:address location="http://localhost:9080/VetService/services/Vet"/>
</port>
</service>
</definitions>
|
Listing 4. Java interface for WSDL using
xsd:anyType
package any;
public interface Vet extends java.rmi.Remote {
public void registerPet(java.lang.Object pet)
throws java.rmi.RemoteException;
} |
Although it may seem like a good idea to use xsd:any in your Web service's XML schema, there are pitfalls to doing so: you are deviating from the intent of WSDL, and the language mapping of xsd:any may not be easy to use. Although I won't go so far as to say using xsd:any is a bad practice, I do hope this tip has given you a better awareness of problems you may encounter before you encounter them.
Learn
- Loosely typed versus strongly typed Web services -- read more about strong versus loose typing and the use of
xsd:any(developerWorks September 2005). - How to use the SAAJ API -- learn to use this API to write Java programs to access
xsd:any(developerWorks, January 2004). - Java API for XML-Based RPC (JAX-RPC) Downloads & Specifications -- find links to the JAX-RPC 1.1 specification from the the java.sun.com Web site.
- SAAJ (SOAP with Attachments API for Java) -- read the specification.
- XML Schema Part 1: Structures and XML Schema Part 2: Datatypes -- read the XML Schema specification.
- XML Schema Primer.
- SOA and Web services -- hosts hundreds of informative articles and introductory, intermediate, and advanced tutorials on how to develop Web services applications.
- Technical briefings -- the IBM developerWorks team hosts hundreds of technical briefings around the world which you can attend at no charge.
Get products and technologies
- WebSphere Application Server -- get a trial download.
- IBM trial software -- build your next development project with software available for download directly from developerWorks.
Discuss
-
developerWorks blogs: get involved in the developerWorks community.
Russell Butek is an IBM Web Services Consultant. He was one of the developers of the IBM WebSphere web services engine. He is also a member the JAX-RPC Java Specification Request (JSR) expert group. He was involved in the implementation of Apache's AXIS SOAP engine, driving AXIS 1.0 to comply with JAX-RPC 1.0. Previously, he was a developer of the IBM CORBA ORB and an IBM representative on a number of OMG task forces, including chairing the portable interceptor task force.