Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Apache SOAP type mapping, Part 1: Exploring Apache's serialization APIs

Learn how to translate the data types in your apps into XML

Gavin Bong (gavinb@eutama.com), Software engineer, eUtama Sdn. Bhd.
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.

Summary:  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.

View more content in this series

Date:  18 Apr 2006 (Published 01 Apr 2002)
Level:  Introductory
Also available in:   Korean  Japanese

Activity:  40241 views
Comments:  

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:
    {uri}localpart.
    

    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

JavaSOAPSerializerDeserializer
Stringxsd:stringbuilt-in*StringDeserializer*
Booleanxsd:booleanbuilt-in*BooleanObjectDeserializer*
booleanxsd:booleanbuilt-in*BooleanDeserializer*
Doublexsd:doublebuilt-in*DoubleObjectDeserializer*
doublexsd:doublebuilt-in*DoubleDeserializer*
Longxsd:longbuilt-in*LongObjectDeserializer*
longxsd:longbuilt-in*LongDeserializer*
Floatxsd:floatbuilt-in*FloatObjectDeserializer*
floatxsd:floatbuilt-in*FloatDeserializer*
Integerxsd:intbuilt-in*IntObjectDeserializer*
intxsd:intbuilt-in*IntDeserializer*
Shortxsd:shortbuilt-in*ShortObjectDeserializer*
shortxsd:shortbuilt-in*ShortDeserializer*
Bytexsd:bytebuilt-in*ByteObjectDeserializer*
bytexsd:bytebuilt-in*ByteDeserializer*
BigDecimalxsd:decimalbuilt-inDecimalDeserializer
GregorianCalendarxsd:timeInstant!CalendarSerializerCalendarSerializer
Datexsd:dateDateSerializerDateSerializer
QNamexsd:QNameQNameSerializerQNameSerializer
  • 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

JavaSOAPSerializer/Deserializer
Java arraysSOAP-ENC:ArrayArraySerializer
java.util.Vector
java.util.Enumeration
{http://xml.apache.org/xml-soap}Vector*VectorSerializer
java.util.Hashtable{http://xml.apache.org/xml-soap}MapHashtableSerializer
java.util.Map{http://xml.apache.org/xml-soap}MapMapSerializer#
Arbitrary JavaBeanUser-defined QnameBeanSerializer
  • *: 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

JavaSOAPSerializer/Deserializer
Byte arraysSOAP-ENC:base64#Base64Serializer
Hexadecimal stringxsd:hexBinaryHexDeserializer
javax.activation.DataSourcexsd:anyTypeMimePartSerializer
javax.activation.DataHandlerxsd:anyTypeMimePartSerializer
  • #: 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
1No Serializer found to serialize a 'xxx' using encoding style 'yyy'
2No Deserializer found to deserialize a ':Result' using encoding style 'yyy'
3No 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
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).

  1. Registry for serializers
  2. Registry for deserializers
  3. Registry for Java2XML
  4. 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:

  1. Use the encodingStyle attribute of the method wrapper accessor if it's available.
  2. 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:

{""}/X

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

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.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and Web services, Java technology
ArticleID=10646
ArticleTitle=Apache SOAP type mapping, Part 1: Exploring Apache's serialization APIs
publish-date=04182006
author1-email=gavinb@eutama.com
author1-email-cc=dwinfo@us.ibm.com

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers