Level: Introductory Gavin Bong (gavinb@eutama.com), Software engineer, eUtama Sdn. Bhd.
01 Apr 2002 Updated 18 Apr 2006 SOAP defines a simple wire protocol for transferring application-level
data. This protocol can easily carry arbitrary Java types as serialized XML, thanks to its rich and extensible type system. In this article, the first of a two-part series on the type system support found in the Apache SOAP toolkit, Gavin Bong will introduce you to the theoretical underpinnings of SOAP's type system. You'll also learn more about SOAP's programmatic support for serialization and deserialization and conclude
with an exploration into the toolkit's internals. A better understanding of how these processes work will help you build your own distributed systems.
When data is exchanged electronically, the endpoints of the exchange need to
agree in advance on two things: an interaction pattern and a type
system. The former is concerned with the architecture of the
communication channel (for example, point-to-point versus many-to-one,
or blocking versus asynchronous). The latter, on the other hand, is an
agreed data format for use in encoding and decoding messages.
In this article, I will describe the type system in SOAP,
as applicable to the Apache SOAP toolkit. Although the
current incarnation of the SOAP toolkit supports both messaging
and RPC interaction patterns, this article will concentrate
on the latter. Unless otherwise stated, the
features discussed in this article apply to Version 2.2 of
Apache SOAP, released in May 2001 (see Resources). Where appropriate, I will highlight
bug fixes or interface changes that have occurred since that release.
As an aside on terminology, let me introduce the namespace prefixes (for both SOAP
and W3C's XML Schemas) and convention for representing QNames (qualified names) that are
used throughout this series.
- A SOAP 1.2 working draft was released in 2001, but our discussion will adhere only to SOAP 1.1 specifics. Thus, the prefixes
SOAP-ENV and SOAP-ENC will reference the namespaces http://schemas.xmlsoap.org/soap/envelope/ and http://schemas.xmlsoap.org/soap/encoding/, respectively.
- The prefixes
xsd and xsi are assumed to reference namespaces http://www.w3.org/2001/XMLSchema and http://www.w3.org/2001/XMLSchema-instance, respectively, unless explicitly stated otherwise.
- QNames with exotic URIs will be written in the following format:
An example: {http://xml.apache.org/xml-soap}Map
SOAP and RPC
When performing RPC calls with SOAP, two types
of payload are exchanged between the Web service
requestor and provider:
- Parameters to remote methods (RPC request)
- Return values (RPC response)
Technically, SOAP attachments and SOAP headers
may also constitute payloads in a SOAP RPC invocation; however,
we can safely ignore them for now. The payloads are usually
encoded using a method colloquially known as Section 5. All SOAP frameworks available today
support this encoding method, although the SOAP specification doesn't specify it as the default encoding.
Section 5 encoding describes a methodology for transforming
an object graph into XML. If the methodology is adhered
to religiously, then the SOAP XML can be reconstructed back
to its original form. Another encoding approach constrains
the payload(s) by using a schema language like W3C's XML Schema.
In the former methodology, all interested parties in a transaction agree on the serialization rules, while in the latter they agree on the literal format of the message. In Part 2 of this series, you'll see an example illustrating schema-constrained SOAP.
Listings 1 and 2 illustrate and explore Section 5 encoding in detail. The Foo JavaBean in Listing 1 is serialized in Listing 2.
Listing 1. Foo JavaBean
class Foo{
int i;
String s;
public Foo() {}
.... /* property mutators and accessors not shown for brevity */
}
|
Listing 2. SOAP representation of a Foo object
<SOAP-ENV:Body>
<ns1:eatFoo
xmlns:ns1="urn:ibmdw:myservice"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<fooParam
xmlns:ns2="http://foo/type"
xsi:type="ns2:Foo">
<i xsi:type="xsd:int">1000</i>
<s xsi:type="xsd:string">Hello World</s>
</fooParam>
</ns1:eatFoo>
</SOAP-ENV:Body>
|
The SOAP XML instance in Listing 2 represents an RPC call that dispatches the method call eatFoo to the Web service
identified by the URI urn:ibmdw:myservice. The elements fooParam,
i, and s are called accessors; they are containers for
values. Multireference accessors are exceptions to this definition; these are empty elements that employ an mechanism like XML Pointer Language (see Resources below for more information)
to reference other elements that contain actual values.
The xsi:type attribute that peppers Listing 2 provides for
subtyping of accessors. Usually, a Web service provider and
requestor have agreed in advance on the data types of parameters
for each RPC call. This prior agreement is sufficient for the
correct deserialization of the SOAP XML instances, even without the
presence of xsi:type attributes. But consider a use case
in which a particular method accepts both strings and integers (for example, the
parameter could be typed as accepting a java.lang.Object parameter).
In this case, in order for the object to deserialize correctly, an explicit xsi:type is needed to assert the type
of the contained value. Accessors with
explicit xsi:type attributes are called polymorphic accessors.
Apache SOAP is designed to treat all accessors as polymorphic.
Version 2.2 continues to generate SOAP XML instances with
polymorphic accessors but can be programmed to unmarshall
without it. I will briefly explain how this is done later on
in this article.
The xsi:type attribute takes QNames as values. Acceptable values
for the xsi:type attribute are: Built-in types from XML Schema Part 2 Datatypes specification.
Apache SOAP supports most of the built-in types and is backward
compatible with types from all versions of the schema Part 2
specification, including:
- Recommendation (2001)
- Candidate Recommendation (October 2000)
- Working draft (1999)
To be precise, Apache SOAP does not cover all of the built-in types,
but it does support those most commonly used. I'll take a look at a detailed manifest of
Section 5-encoded types supported by Apache SOAP in the next section.
Arbritary QNames denoting a user-defined type.
These are used to represent compound types like ns2:Foo in Listing 2.
The serialized form of this type could be defined by a schema document, or, as in
Apache SOAP, by a custom (de)serializer.
SOAP 1.1 extension types.
These include SOAP-ENC:Array, which represents an ordered sequence of array members, and SOAP-ENC:base64, which represents a base-64 encoded string.
The encodingStyle attribute in Listing 2 is used to indicate
the serialization scheme used. For example, Section 5 encoding
is represented by the URI http://schemas.xmlsoap.org/soap/encoding/.
Apache SOAP has built-in support for three encodingStyles:
Section 5, XMI, and literal XML. Custom encodingStyles are also supported.
However, Apache SOAP's Section 5 encoding support is not
comprehensive. It is still missing some features, like support for
sparse, partially transmitted, and multidimensional arrays.
Up till now, I've been implying that endpoints of data exchange will magically be
able to (de)serialize Section 5-encoded types; but that premise actually
depends on some out-of-band agreement on the data model of the entities
that are being passed back and forth. This basically means that endpoints
need to agree on "well-known" types. There is, however, an alternative.
Software developers who want to dispense with Section 5's limitations can turn to
schema-based serialization. This method works by publishing the interfaces of the
SOAP service along with the schema(s) for the request/response messages. Web Services Definition Language (WSDL) is the
current de facto standard for this purpose. Apache SOAP is not WSDL aware,
but Axis (see Resources), Apache SOAP's successor toolkit, is.
Section 5 encoding
SOAP doesn't define any language bindings for the SOAP types described by Section 5
encoding. Instead, its types are general enough to model some of the typical data types
found in the Java programming language and most other mainstream programming languages. In this section, we will
explore Apache SOAP's support for the encoding and decoding of Java primitives and
arbitrary Java classes.
But first let's define the terms. Serialization is the process of
converting a Java object into an XML instance, and deserialization is the process
of reconstructing the Java object from the XML. Apache SOAP utilizes constructs called serializers
and deserializers to perform these actions. I will use the shortened word (de)serializer
to denote the phrase deserializer and serializer throughout this article.
The base set of (de)serializers included with the Apache SOAP toolkit can handle three groups of data types: simple types,
compound types, and special types. The terms simple types and compound types
are taken directly from the SOAP 1.1 specification, and their meaning in this context is the same as their meaning in the spec. I've coined the term special types to denote Java types that do not fit in nicely with the previous two groups.
Simple types
Apache SOAP serializes these types directly as accessors with only text nodes
as children. In general, the XML instances have the following form:
<accessor xsi:type="qname">
<!-- data -->
</accessor>
|
Table 1. Simple types supported in Apache SOAP
| Java | SOAP | Serializer | Deserializer | String | xsd:string | built-in* | StringDeserializer* | Boolean | xsd:boolean | built-in* | BooleanObjectDeserializer* | boolean | xsd:boolean | built-in* | BooleanDeserializer* | Double | xsd:double | built-in* | DoubleObjectDeserializer* | double | xsd:double | built-in* | DoubleDeserializer* | Long | xsd:long | built-in* | LongObjectDeserializer* | long | xsd:long | built-in* | LongDeserializer* | Float | xsd:float | built-in* | FloatObjectDeserializer* | float | xsd:float | built-in* | FloatDeserializer* | Integer | xsd:int | built-in* | IntObjectDeserializer* | int | xsd:int | built-in* | IntDeserializer* | Short | xsd:short | built-in* | ShortObjectDeserializer* | short | xsd:short | built-in* | ShortDeserializer* | Byte | xsd:byte | built-in* | ByteObjectDeserializer* | byte | xsd:byte | built-in* | ByteDeserializer* | BigDecimal | xsd:decimal | built-in | DecimalDeserializer | GregorianCalendar | xsd:timeInstant! | CalendarSerializer | CalendarSerializer | Date | xsd:date | DateSerializer | DateSerializer | QName | xsd:QName | QNameSerializer | QNameSerializer |
-
built-in: Inner class implemented in
org.apache.soap.encoding.SOAPMappingRegistry.
-
*: Since Apache SOAP version 2.1.
-
!: In January 2001,
timeInstant was renamed dateTime when the W3C XML Schema Datatypes specification (see Resources) transitioned to proposed recommendation status. Apache doesn't support xsd:dateTime yet (see Resources).
Before we move on, let's more closely examine three points from Table 1. Firstly, astute readers
will notice that char is not supported (see Resources).
Fortunately, writing a custom (de)serializer for it is a trivial matter. Secondly, an
update to BooleanDeserializer allows it to recognize the set of values { "1", "t", "T" } to represent the boolean literal true and { "0", "f", "F" } for false. Thirdly, the recipient of the RPC invocation will deserialize to primitives by
default. To illustrate, let's suppose a SOAP server exposes a single method, like this:
echoBoolean( Boolean b ) { .. }
|
When you invoke echoBoolean with a Boolean parameter,
a SOAP Fault is generated:
Exception while handling service request:
com.raverun.bool.Service.echoBool(boolean) -- no signature match
|
Fortunately, there is a suite of deserializers (xxxObjectDeserializer)
which will return the corresponding wrapper objects. In order for that
to happen, you'll need to override its default deserialization behavior.
You'll see how this is done later on in this article.
Compound types
From a Java perspective, compound types are types with
constituent elements. These elements are either identified purely by name (for example, a Java class
with several member properties) or by ordinal position
(for example, a List data structure like Array or Vector). Listing 3 shows an XML instance of a compound type.
Listing 3. XML instance for java.util.Hashtable
<hash xmlns:ns2="http://xml.apache.org/xml-soap" xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">2</key>
<value xsi:type="xsd:string">two</value>
</item>
<item>
<key xsi:type="xsd:string">1</key>
<value xsi:type="xsd:string">one</value>
</item>
</hash>
|
Table 2. Section 5 encodings for compound types
| Java | SOAP | Serializer/Deserializer | | Java arrays | SOAP-ENC:Array | ArraySerializer | java.util.Vector
java.util.Enumeration | {http://xml.apache.org/xml-soap}Vector* | VectorSerializer | java.util.Hashtable | {http://xml.apache.org/xml-soap}Map | HashtableSerializer | java.util.Map | {http://xml.apache.org/xml-soap}Map | MapSerializer# | | Arbitrary JavaBean | User-defined Qname | BeanSerializer |
-
*:
VectorSerializer in Apache 2.1 serializes to a SOAP Array. However, in 2.2, it was introduced as its own Apache SOAP proprietary type.
-
#:
MapSerializer actually delegates calls to HashtableSerializer.
As you can see in Table 2, BeanSerializer can be used to send arbitrarily
complex Java classes. But a serialized Java class must comply with the JavaBeans design
pattern; specifically, it must have a public no-arg constructor
and getter/setter methods for every property you want serialized. In situations in which your getter/setter methods
do not satisfy the JavaBeans naming convention, you can expose them to the BeanSerializer
via a BeanInfo class. For example, if you need to
serialize class Foo, create a class named FooBeanInfo
that implements the interface java.beans.BeanInfo. See Listing 4 for an example.
Listing 4. FooBeanInfo class
import java.lang.reflect.*;
import java.beans.*;
/*
* Foo has a single String property by the name of "s".
* And its setter and getter methods are called "_setS" and "_getS"
* respectively, thus violating the JavaBean spec. Passing Foo to BeanSerializer
* will cause this exception to be thrown:
* Error: Unable to retrieve PropertyDescriptor for property 's'
* of class "Foo".
*/
public class FooBeanInfo extends SimpleBeanInfo{
private final static Class c = Foo.class;
public PropertyDescriptor[] getPropertyDescriptors()
{
try{
Class[] sp = new Class[] {String.class};
Method s_setter = c.getMethod( "_setS", sp );
Method s_getter = c.getMethod( "_getS", null );
PropertyDescriptor spd = new PropertyDescriptor("s", s_getter, s_setter);
PropertyDescriptor[] list = { spd };
return list;
}catch (IntrospectionException iexErr){
throw new Error(iexErr.toString());
}catch (NoSuchMethodException nsme){
throw new Error(nsme.toString());
}
}
}
|
Special types
There are a number of Java types that don't quite fit either of the above categories; SOAP can handle these types as well.
Null and void
Apache SOAP will serialize object references with null values by adding
a xsi:null attribute, set to "true". Its default behavior is to ignore other
possible variations of the xsi:null attribute, such as:
-
xsi:nil="true" (as per XML Schema Part 1) (see Resources)
-
xsi:null="1" (as per SOAP 1.1) (see Resources)
Listing 5 includes an example of a serialized object reference including a null value. Listing 5. SOAP representation of a sparse vector
<myvec xmlns:ns2="http://xml.apache.org/xml-soap" xsi:type="ns2:Vector">
<item xsi:type="xsd:string">Hello World</item>
<item xsi:type="xsd:anyType" xsi:null="true"/>
</myvec>
|
In Apache SOAP 2.1, sending vectors with null members caused a SOAPException,
as no deserializer existed then to deserialize a null reference. The
UrTypeDeserializer was introduced for this purpose. The VectorSerializer
maps a null reference to xsd:anyType (xsd:ur-type is deprecated in the
2001 XML Schema recommendation), the root class in the XML Schema built-in datatype hierarchy.
When handling string parameters, Apache SOAP does not deserialize to a null object reference
if the accessor is an empty element without the xsi:null attribute. For example,
if a remote method expects a String parameter:
public void eat(String s) //1
|
and you assume this request is sent to a Web service as:
<ns1:eat>
<s xsi:type="xsd:string"/>
</ns1:eat>
|
it will deserialize to an empty String.
Binary data
If you need to transfer binary blobs, you have three options:
- Byte arrays
- Hexadecimal strings
- MIME attachments
Table 3. Sending blobs
| Java | SOAP | Serializer/Deserializer | | Byte arrays | SOAP-ENC:base64# | Base64Serializer | | Hexadecimal string | xsd:hexBinary | HexDeserializer | javax.activation.DataSource | xsd:anyType | MimePartSerializer | javax.activation.DataHandler | xsd:anyType | MimePartSerializer |
-
#: The latest version of
SOAPMappingRegistry supports {http://www.w3.org/2001/XMLSchema}base64Binary -- a supertype of SOAP-ENC:base64 -- during deserialization.
A wrapper class, org.apache.soap.encoding.Hex, is used
to encapsulate hexadecimal strings. This is the class that gets
(de)serialized. The wrapper class will accept both lower- and upper-case letters for
hexadecimal values a through f. Just make sure that your
hexadecimal string contains an even number of characters.
Apache SOAP supports the embedding of a SOAP envelope inside a MIME/multipart related message.
Binary data can then be carried as MIME attachments. This is in accordance with the
SOAP Messages with Attachments (SwA) specification (see Resources).
Literal XML
If you send an XML fragment as a string, the built-in string serializer
will escape all characters that are illegal as element content
by replacing them with their predefined string entities --
< will replace <, for instance. To send literal XML embedded
within the SOAP RPC structure, you have two options. You could wrap the XML string within CDATA sections. This allows
you to send poorly formed XML also. The second method is to load
your XML fragment into a DOM and send the document element
as the parameter. Apache SOAP will serialize org.w3c.dom.Element
instances via the XMLParameterSerializer.
Type mapping patterns
When programming SOAP clients and servers with Apache SOAP, you will most
likely have encountered the dreaded java.lang.IllegalArgumentException with
messages from the list in Table 4
.
Table 4. Common type mapper error messages
| Message | | 1 | No Serializer found to serialize a 'xxx' using encoding style 'yyy' | | 2 | No Deserializer found to deserialize a ':Result' using encoding style 'yyy' | | 3 | No mapping found for 'xxx' using encoding style 'yyy' |
The serialization and deserialization mechanics in Apache SOAP rests on the
availability of type mappings defined in a registry. A type mapping defines
how to transform a Java class (or primitive) into XML and vice versa. The
registry is an instance of the class org.apache.soap.encoding.SOAPMappingRegistry.
By default, both the SOAP client and SOAP server will inherit the same set
of type mappings.
Figure 1. Serialization/deserialization in context

For the following discussion, I will use the terms inbound and outbound
to describe the direction of SOAP message processing. For example, in the
context of a SOAP server, serialization refers to the mechanics of generating
the outbound message. This simple model ignores intermediary SOAP servers without
any detriment to our discussion. Figure 1 depicts the architecture under discussion.
Before I describe the programmatic syntax for interacting with the
type mapping registry, it will be helpful to have some idea of the
internal implementations of the registry. The registry that the developer
interacts with is actually a facade for four internal Hashtables (see list below).
- Registry for serializers
- Registry for deserializers
- Registry for Java2XML
- Registry for XML2Java
Serialization and deserialization are symmetrical functions; thus, I will
only discuss how serialization type mappings are stored and hope that you'll be
able to extrapolate the deserialization case from my explanation. A serialization type mapping
is represented by entries in the three hashtables -- A, C, and D -- from the list above. Consider as an example a serializer, FooSerializer, that
serializes Foo.class objects to the SOAP type "{http://someuri}foo".
If you consider the string k to represent the unique key for this
type mapping, these are the mappings created for serialization purposes:
- Hashtable A:
k maps to FooSerializer.class
- Hashtable C:
k maps to {http://someuri}foo
Both Hashtables A and C are keyed by a shared string generated by concatenating the Java runtime type and the encodingStyleURI. Hashtable A maintains a collection of Java objects that
implement the interface org.apache.soap.util.xml.Serializer, while Hashtable C manages a collection of QNames.
During the serialization process, the registries are interrogated with two API calls:
Serializer querySerializer( javaType, encodingStyleURI )
QName queryElementType (javaType, encodingStyleURI )
|
When Apache SOAP is directed to serialize a Foo instance, it invokes the querySerializer
method and gets back a FooSerializer. FooSerializer is then called upon to marshall
the object; that's when it will call queryElementType to retrieve the xsi:type
attribute value.
If querySerializer fails to return any Serializer instance, you will get
error message 1 in Table 4. On the other hand, if queryElementType does not return
a matching QName, error message 3 is returned.
Defining type mappings on the SOAP client
You can augment the registry to handle your special types
by calling the mapTypes() method on a SOAPMappingRegistry instance.
The method signature is as follows:
mapTypes( String, QName, Class, Serializer, Deserializer );
|
If you want the mapping to be registered into Hashtables C and D
simultaneously, then the QName and Class parameters are mandatory. Listing 6 contains examples of default type mappings in Apache SOAP.
Listing 6. Example client maptypes
HashtableSerializer hashtableSer = new HashtableSerializer();
mapTypes("http://schemas.xmlsoap.org/soap/encoding/",
new QName("http://xml.apache.org/xml-soap", "Map"),
Hashtable.class,
hashtableSer,
hashtableSer);
mapTypes("http://schemas.xmlsoap.org/soap/encoding/",
new QName("http://schemas.xmlsoap.org/soap/encoding/", "Array"),
null,
null,
new ArrayDeserializer());
mapTypes( encStyleURI, Qname, Foo.class, null, null );
|
The Hashtable mapping in Listing 6 is an example of a symmetric mapping, where
both inbound and outbound processing are registered. The second mapping is an
asymmetric mapping; it merely registers the ArrayDeserializer into Hashtable B. Array serialization
takes place within SOAPMappingRegistry.querySerializer(), which uses
introspection to figure out whether the Java type is an array and then returns
an new instance of ArraySerializer then and there. The last mapping
is purely an association.
Defining type mappings on the SOAP server
On the server side, the type mappings are defined in an XML file called
the deployment descriptor. Apache SOAP actually builds a SOAPMappingRegistry
instance from the contents of the deployment descriptor.
A schema for the XML grammar is available in the source distribution that accompanies this article (see Resources).
You can see an extract from the schema in Listing 7, which shows the attributes for the
map element; its attributes mirror the parameters of the mapTypes
method of SOAPMappingRegistry. The only difference is that qname
is mandatory for map.
Listing 7. Schema fragment for map element in the Apache SOAP deployment descriptor
<complexType name="mapType">
<attribute name="encodingStyle" type="xsd:anyURI"/>
<attribute name="qname" type="xsd:string"/>
<attribute name="javaType" type="xsd:string" use="optional"/>
<attribute name="java2XMLClassName" type="xsd:string" use="optional"/>
<attribute name="xml2JavaClassName" type="xsd:string" use="optional"/>
</complexType>
|
Compiled custom type mappings
It is possible to bypass the tedious manual declaration of type mappings by
consolidating them inside a subclass of SOAPMappingRegistry. This subclass
can then be used on both clients and servers. Listing 8 includes a code sample for a SOAPMappingRegistry subclass that provides its own
array (de)serializer.
Listing 8. Subclassing public class MySmr extends SOAPMappingRegistry
public MySmr()
{
super();
registerMyMappings();
}
public MySmr(String schemaURI)
{
super(schemaURI);
registerMyMappings();
}
private void registerMyMappings()
{
MyArraySerializer arraySer = new MyArraySerializer();
mapTypes(Constants.NS_URI_SOAP_ENC,
new QName(Constants.NS_URI_SOAP_ENC, "Array"),
null,
arraySer,
arraySer);
}
}
|
To use this custom SOAPMappingRegistry on the invoker side, pass it as the parameter
to the Call object's setSOAPMappingRegistry() method. On the provider side,
delete all of your individual map elements and instead modify your deployment descriptor as shown
in Listing 9.
Listing 9. Deployment descriptor changes
<isd:service ...>
// other elements here
<isd:mappings defaultRegistryClass="com.raverun.array.MySmr" />
</isd:service>
|
How does Apache SOAP deal with different schema namespaces?
In Listing 9, you'll notice that the SOAPMappingRegistry class has
an overloaded constructor that takes a String parameter. That
parameter is actually a URI denoting specific versions of W3C's XML Schema language.
The versions accepted by Apache SOAP are predefined finals in org.apache.soap.Constants:
-
Constants.NS_URI_1999_SCHEMA_XSD = http://www.w3.org/1999/XMLSchema
-
Constants.NS_URI_2000_SCHEMA_XSD = http://www.w3.org/2000/10/XMLSchema
-
Constants.NS_URI_2001_SCHEMA_XSD = http://www.w3.org/2001/XMLSchema
If you instantiate SOAPMappingRegistry with its no-arg constructor,
Apache SOAP will serialize types to the 1999 namespace by default.
Invoking the overloaded constructor actually only overrides the xsd
namespace; the xsi namespace declared in the SOAP envelope
remains unchanged. This is because the envelope takes its namespace prefix
mappings directly from Constants.NS_URI_CURRENT_SCHEMA_XSD
and Constants.NS_URI_CURRENT_SCHEMA_XSI, which point to the 1999 namespace.
This shortcoming is fixed in the latest CVS source release (see Resources). But from the
deserialization point of view, Apache SOAP is capable of gracefully handling
schema types from any of the three versions listed.
Encoding style and type mappings
From our knowledge of the internals of the type-mapping registry,
it is evident that the encodingStyleURI plays a big part in
determining how serialization and deserialization should proceed.
In this section, I will highlight some programming issues that affect the use of encodingStyleURI.
First, the SOAP 1.1 specification clearly states that "there is
no default encoding defined for a SOAP message." Thus, in Apache SOAP,
the default encodingStyleURI is null. If you do not initialize it to a
value in the Call object or in the declaration of each parameter, you
can do so by calling the setDefaultEncodingStyle method of SOAPMappingRegistry.
This is equivalent to adding a locally scoped encodingStyleURI attribute
to all your parameter accessors.
Second, consider a scenario in which the encodingStyle of the SOAP response
and SOAP request need to be different. For discussion's sake,
consider the method countKids() in Listing 10. Its inbound and outbound
encodingStyles are literal XML and Section 5, respectively.
Listing 10. Code fragment of an example SOAP server
public int countKids( Element el ){
return( DOMUtils.countKids(el, Node.ELEMENT_NODE) );
}
|
If you were to invoke countKids() with the code in Listing 11, a SOAP
fault is returned with the message: java.lang.IllegalArgumentException:
I only know how to serialize an 'org.w3c.dom.Element'.
Listing 11. Code fragment for the RPC invocation
Call call = new Call();
Vector params = new Vector();
params.addElement( new Parameter("xmlfile",
Element.class,
e,
Constants.NS_URI_LITERAL_XML) );
call.setParams( params );
call.invoke ( url, "" );
|
To understand why this error occurs, you'll need to understand the algorithm that Apache SOAP
(on the SOAP server) uses to determine what encodingStyle to use on outbound messages:
- Use the
encodingStyle attribute of the method wrapper accessor if it's available.
- If this attribute is missing, deduce the
encodingStyle from
the first parameter.
So, if your method accepts multiple parameters, you
might want to reorganize their order if you're not setting Call's encodingStyle attribute explicitly. To be completely safe, set the encodingStyle in the
Call object to correspond to the return type of the remote method.
Mixing encodingStyles in a SOAP message
First and foremost, the SOAP spec doesn't disallow mixing encodingStyles within
a parameter; the encodingStyle attribute's scope behaves like a namespace declaration
anyway. An useful case of such mixing is an array of SVG fragments. At first,
it might seem plausible to serialize the array using Section 5 and its SVG members using
literal XML. Unfortunately, this will not work, since all of the Section 5
serializers are hardcoded to only work with types registered under the Section 5
encodingStyles. To put it in another way, everything within a Section 5-encoded
XML stream is immutable and cannot contain XML fragments encoded using a different
encodingStyle. Also, a recent bug fix (see Resources) made sure that
values within compound data structures like Array, Hashtable, and
Vector cannot assume any encodingStyle other than Section 5.
Miscellaneous type mapping issues
We'll conclude this article with a few problems and quirks you might encounter with SOAP type mapping.
Relaxing xsi:type during deserialization
When processing inbound messages generated by Microsoft's SOAP toolkits, you'll likely
encounter the following error:
No Deserializer found to deserialize a ':Result' using encoding style 'yyy'
|
This classic interoperability problem is caused by the absence of the xsi:type attributes
in the accessors. The solution implemented in the Apache SOAP 2.1 release is to dynamically create the xsi:type
value following this rule: If the accessor for the parameter is not under the scope of any
namespace, use the following QName:
where X represents the tagName of the accessor
and "" an empty URI. In place of the empty URI, use the namespace URI of the accessor being
deserialize if it exists. With this in place, type mappings are generated to match these
ad hoc QNames to (de)serializers. Listing 12 demonstrates sample type mappings for both
client and server. Listing 12. Code fragment for the RPC invocation
[Client]
SOAPMappingRegistry smr = new SOAPMappingRegistry ();
StringDeserializer sd = new StringDeserializer ();
smr.mapTypes (Constants.NS_URI_SOAP_ENC,
new QName ("", "Result"), null, null, sd);
[Server]
<isd:mappings>
<isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:x="" qname="x:Book"
javaType="com.raverun.Book"
xml2JavaClassName="org.apache.soap.encoding.soapenc.BeanSerializer"/>
<isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:x="" qname="x:price"
xml2JavaClassName="org.apache.soap.encoding.soapenc.FloatObjectDeserializer"/>
<isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:x="" qname="x:isbn"
xml2JavaClassName="org.apache.soap.encoding.soapenc.StringDeserializer"/>
</isd:mappings>
|
The type mappings need to conform to the following conventions:
- For primitive types and their wrapper objects (for example,
int and Integer), do not specify the javaType in the type mapping.
- For compound types, if your deserializer requires knowledge of the
javaType
when performing its duties (for example, org.apache.soap.encoding.soapenc.BeanSerializer), then you may specify a javaType.
More details on this behavior can be gleaned from a posting by Sanjiva Weeraweena to the soap-dev mailing list and from an article by Jim Snell (see Resources for links to both).
Multireference types
Multireference types are not a new type grouping but should be considered a facet
of simple and compound types. They're useful for preserving identity between parameters,
specifically parameters those exhibiting DCE-style [ptr] semantics.
Apache SOAP is capable of deserializing multireference types but does not serialize to them.
Multireference types are serialized into two parts:
the parameter accessor (an empty element) and the value accessor.
In Listing 13, the element varString is the parameter accessor and greeting is the value accessor. Notice that the value accessor is an immediate sibling element to the method wrapper accessor ns1:echoString. This is important
because Apache SOAP assumes that the root of the serialization graph (the method wrapper
accessor) is the immediate child of SOAP-ENV:Body. If the
value accessor were to come first, you would get a Server.BadTargetObjectURI fault. The SOAP
specification provides an attribute, SOAP-ENC:root, to
indicate that an element is not the serialization root, but Apache SOAP does not
recognize it.
Listing 13. An example of a multireference parameter
<SOAP-ENV:Body>
<ns1:echoString
xmlns:ns1="http://soapinterop.org"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<varString href="#String-0" />
</ns1:echoString>
<greeting
xsi:type="xsd:string"
id="String-0">Hello World</greeting>
</SOAP-ENV:Body>
|
XMI encoding
XMI (XML Metadata Interchange) is an OMG standard for sharing UML models among
applications. Its most common uses are in UML modelling applications that
export and import their models to and from XMI files. A Java Object Bridge (JOB)
in the IBM XMI Framework allows Java objects to be (de)serialized into XMI format.
XMI can thus be considered an alternative to Section 5 encoding, though it is
seldom used.
Wrapping up and looking ahead
In this article, I've explored most of Apache SOAP's out-of-the-box API support for
(de)serializing Java types. I've also covered some of the idiosyncracies that may inhibit
interoperability with other Java- or non-Java-based SOAP toolkits. In the next installment, I will
introduce a cookbook that will guide you in writing your own (de)serializers. You'll also see how you can work with
non-Section 5 encodings by utilizing a schema language.
Resources -
Apache SOAP is an open source SOAP toolkit for Java programming language.
-
Apache Axis is the next-generation toolkit that supercedes Apache SOAP.
- Brendan Macmillan's Java Serialization to XML (JSX) toolkit contains some useful tools.
- Download an early access release of Sun's Java Architecture for XML Binding (JAXB).
-
Castor is an open source data binding framework for Java programming language.
-
Schema2Java is a commercial XML data binding product from Creative Science Systems.
- Download the sample code that accompanies this series as PurchaseOrder.zip or PurchaseOrder.tar.gz. I'll go into the code in detail in the next installment; however, the package includes a schema for the XML grammar that you may wish to examine now.
- To learn more about XMI (XML Metadata Interchange), check out the IBM XMI Framework.
- The SOAP 1.1 specification is available as a W3C Technical Note.
-
This post by Sanjiva Weerawarana describes the loosening of
xsi:type requirement in Apache SOAP.
- Apache SOAP's interoperability with other toolkits is covered in "The Web services insider, Part 3" by James Snell (developerWorks, May 2001).
- "SOAP Messages with Attachments," a W3C note, describes how SOAP is embedded within MIME.
- The W3C XML Schema, Part 1, describes the core XML Schema concepts and syntax.
- The W3C XML Schema, Part 2, describes data types supported in the XML Schema.
- Apache SOAP bug report #2865 describes the nonsupport for the
char primitive.
- Apache SOAP bug report #3000 describes the
timeInstant bug.
- Apache SOAP bug report #2388 describes the use of old XML Schema namespace URIs.
- Apache SOAP bug report #2470 describes the
HashtableSerializer problem of mixing SOAP and literal XML encoding styles.
About the author  | |  | Gavin Bong is a Java developer from Kuala Lumpur, Malaysia.
His areas of interest include service-oriented architectures and wireless Java. You can contact Gavin at gavinb@eutama.com. He would like to thank
Ying Pee Eng for her help on polishing up Figure 1. |
Rate this page
|