Part 1 of this series discussed the importance of designing a Web Services Description Language (WSDL) and XML Schema data types (XSD) before code writing, the rationale in completely moving toward the Document/literal style, and the necessity in testing the WS-I Basic Profile conformance when developing Web services. This part illustrates the use and impact data types have on interoperability.
The input parameters and return values of data types of a Web service operation have a great impact on the interoperability of the Web service. Web services serve as transport for an XML document exchange. When data objects push down into a Web service stack, they serialize into XML data representations. The Web service stack on the other side needs to know exactly how to map those XML data representations to the needs of the environment of the local application (for example, the de-serialization of XML data). The XML Schema definitions are what drives the mappings. The objective of the XSDs is to guarantee that the type sent has a reproducible version on the other end. But due to the implementation difference in the underlying technologies (Java™ 2 Platform, Enterprise Edition (J2EE) technology versus Microsoft® .NET), the mappings between XSDs and native data types on those platforms might be different. Some of the differences might result in de-serialization failure, while others might cause information distortion.
In the following sections, I discuss several interoperability issues in relation to data types such as:
- The improbability of vendor tools to accurately interpret XML Schemas representing weakly-typed collection objects and mapping them to the correct native data types.
- XML representations of an array with null elements differ between .NET and IBM® WebSphere®.
- Translation issues resulting in the loss of information or precision due to a lack of a one-to-one mapping shared by native and XSD data types.
Collection of complex data types in the signature of a Web service method
Collection objects might contain elements of any data types. Thus, many consider them as weakly-typed data structures. That makes them a wonderful programming tool. In object-oriented programming, there are rich libraries of collection types. In Java for example, there are:
java.util.HashtableVectorsHashmapSetArrayList
While in C#, there are:
System.Collections.HashtableSortedListQueueStackArrayList
If exposed across Web services, these collection types can cause insurmountable problems. The problem lies in how the receiving side is able to understand the serialized Simple Object Access Protocol (SOAP) messages that contain the weakly-typed object elements and native data types.
Even though some collection types look extremely similar between languages, such as System.Collections.ArrayList in C# and java.util.ArrayList in Java, remember that the elements in the collections are generic references. To accurately unmarshall the XML representation of a collection, consumers must have prior knowledge of the original concrete types. The burden is on the toolkit developers to interpret the XML Schemas published by the Web services providers and map the SOAP messages to the native data - - not an easy task for the weakly-typed collections.
Now, let's take a look at what the XML Schemas look like for the Collection types. This time,
consider a Web service deployed on the Microsoft .NET framework. Suppose that an InventoryService accepts a System.Collections.ArrayList of Product as arguments, sets the new price by increasing 10 percent for each product in the ArrayList, and returns the new object of System.Collections.ArrayList type.
Listing 1. An Inventory Web service in C#
namespace Inventory
{
[WebService(Namespace="http://services.inventory")]
public class InventoryService: WebService
{
//increase the product price by 10 percent
private static float inc_rate = 0.10F;
public struct Product {
public string name;
public int qty;
public float price;
}
[WebMethod]
[XmlInclude(typeof(Product))]
public ArrayList updateProductPrice(ArrayList products)
{
ArrayList newList = new ArrayList();
IEnumerator eList = products.GetEnumerator();
while(eList.MoveNext())
{
Product item = (Product)(eList.Current);
item.price = item.price * (1 + inc_rate);
newList.Add(item);
}
return newList;
}
}
}
|
The WSDL engine in the .NET framework generates the following XML Schema for the
Collection type, ArrayList, and the Product complex type :
Listing 2. The XML Schema for the ArrayList and Product
1. <types> 2. <s:schema xmlns:s="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://services.inventory"> 3. <s:element name="updateProductPrice"> 4. <s:complexType> 5. <s:sequence> <s:element maxOccurs="1" minOccurs="0" name="products" type="s0:ArrayOfAnyType"/> 6. </s:sequence> 7. </s:complexType> 8. </s:element> 9. <s:complexType name="ArrayOfAnyType"> 10. <s:sequence> 11. <s:element maxOccurs="unbounded" minOccurs="0" name="anyType" nillable="true"/> 12. </s:sequence> 13. </s:complexType> 14. <s:complexType name="Product"> 15. <s:sequence> 16. <s:element maxOccurs="1" minOccurs="0" name="name" type="s:string"/> 17. <s:element maxOccurs="1" minOccurs="1" name="qty" type="s:int"/> 18. <s:element maxOccurs="1" minOccurs="1" name="price" type="s:float"/> 19. </s:sequence> 20. </s:complexType> 21. <s:element name="updateProductPriceResponse"> 22. <s:complexType> 23. <s:sequence> <s:element maxOccurs="1" minOccurs="0" name="updateProductPriceResult" type="s0:ArrayOfAnyType"/> 24. </s:sequence> 25. </s:complexType> 26. </s:element> 27. </s:schema> 28. </types> |
Lines 9 through 13 (see Listing 2) define a complex type, xsd:ArrayOfAnyType, with an unbounded sequence of elements anyType. The ArrayList of Products has been translated into a sequence of anonymous elements in the XML Schema definition. This is expected; however, it poses two problems. First, other Collection types will also be translated into xsd:ArrayOfAnyType. Therefore, how does the SOAP toolkit on another platform decide which Collection type to map it to?
Secondly, the xsd:anyType is the default type when the type is not specified. Line 11 in Listing 2 is expected because the objects in a Collection are generic references -- the types are not known until run-time. The problem occurs when the SOAP toolkit in another platform receives the serialized objects. How can you find the right serializer to de-serialize the XML payload back to the concrete objects?
In fact, JAX-RPC generates the following helper class from the xsd:ArrayOfAnyType schema in Listing 2.
Listing 3. The resulting helper class for the xsd:ArrayOfAnyType schema
public class ArrayOfAnyType implements java.io.Serializable {
private java.lang.Object[] anyType;
<!-- The setter, getter, equals() and hashCode() methods -->
}
|
From Listing 3, you can see that the ambiguities in the xsd:ArrayOfAnyType schema have caused the JAX-RPC tool to generate the helper class with an generic java.lang.Object[] array as its private field instead of the concrete Product array.
To resolve this ambiguity, you can use ArrayOfRealType instead of the xsd:ArrayOfAnyType. You should only expose the simple array of concrete types (that is, Product[]) as the signature of Web service methods.
For the Web service in Listing 1, a facade method can be exposed:
Listing 4. Facade to expose the simple array Product[]
[WebMethod]
[XmlInclude(typeof(Product))]
public Product[] updateProductPriceFacade(Product[] products)
{
ArrayList alist = new ArrayList();
IEnumerator it = products.GetEnumerator();
while (it.MoveNext())
alist.Add((Product)(it.Current));
alist = updateProductPrice(alist);
Product[] outArray = (Product[])alist.ToArray(typeof(Product));
return outArray;
}
|
The new schemas for the input and output message parts are:
Listing 5. The XML Schema for the new Web service in Listing 4
1. <s:element name="updateProductPriceFacade"> 2. <s:complexType> 3. <s:sequence> 4. <s:element minOccurs="0" maxOccurs="1" name="products" type="s0:ArrayOfProduct" /> 5. </s:sequence> 6. </s:complexType> 7. </s:element> 8. <s:complexType name="ArrayOfProduct"> 9. <s:sequence> 10. <s:element minOccurs="0" maxOccurs="unbounded" name="Product" type="s0:Product" /> 11. </s:sequence> 12. </s:complexType> 13. <s:element name="updateProductPriceFacadeResponse"> 14. <s:complexType> 15. <s:sequence> 16. <s:element minOccurs="0" maxOccurs="1" name="updateProductPriceFacadeResult" type="s0:ArrayOfProduct" /> 17. </s:sequence> 18. </s:complexType> 19. </s:element> |
From Line 8 to Line 12, the xsd:ArrayOfProduct schema is created to represent the concrete Product array. No ambiguity is present in the schema. As a result, the Web service client will have no problem in de-serializing the array of Products.
The XML representations of an array with null elements are different between .NET and WebSphere. Consider the Java Web service method in Listing 6.
Listing 6. A Java method returning an array with a null element
public String[] returnArrayWithNull() {
String[] s = new String[3];
s[0] = "ABC";
s[1] = null;
s[2] = "XYZ";
return s;
}
|
The String element, s[1], is assigned a null value. When a .NET client invokes this Web service method hosted on the WebSphere platform, the String array is serialized as:
Listing 7. The Web service response message from WebSphere
<soapenv:Body> <returnArrayWithNullResponse xmlns="http://array.test"> <returnArrayWithNullReturn>ABC</returnArrayWithNullReturn> <returnArrayWithNullReturn xsi:nil="true"/> <returnArrayWithNullReturn>XYZ</returnArrayWithNullReturn> </returnEmptyStringResponse> </soapenv:Body> |
The second element in the array is set to xsi:nil="true". That's fine in Java; a Java client would have correctly de-serialized it back to a null String for the second element in the array. However, the .NET client de-serializes it into a zero length string instead of a null string. Empty and Null are completely different beasts in any object-oriented programming language.
Now consider another Web service method hosted on WebSphere, as shown in Listing 8.
Listing 8. A Java method with arrays and its input and output signatures
public String[] processArray(String[] args) {
//do something to the input array and return it back to the client
return args;
}
|
This time, the Web service method takes an array as input, processes it, and returns the array back to the client. Suppose a .NET client sends out an array with a null element as shown in the code in Listing 9.
Listing 9. A .NET client sends an array with a null element
TestArrayService proxy = new TestArrayService();
string[] s = new string[3];
s[0] = "abc";
s[1] = null;
s[2] = "xyz";
// Console.WriteLine("the length of the input array = " +
s.GetLength(0));
string[] ret = proxy.processArray(s);
// Console.WriteLine("the length of the output array = " +
ret.GetLength(0));
|
Listing 10 shows the SOAP request message from the .NET client.
Listing 10. The SOAP request message sent by the .NET client
<soap:Body> <processArray xmlns="http://array.test"> <args>abc</args> <args>xyz</args> </processArray> </soap:Body> |
The null element, s[1], is omitted from the SOAP request sent by the .NET client. As a result, the length of the returned array is no longer the same as the length of the original array. If this array's length or element index is important to the client's logic, then the client will fail.
The best practice is not to pass an array with null elements between Web service clients and servers.
Even primitive types can cause trouble
XML Schema, by providing a rich type model, eases interoperability. You can construct WSDL messages and operations because the XML Schema identifies specific data types that a Web service uses. XSD offers a wide range of types and simple structs. However, each programming language has a set of native data types. A one-to-one mapping is not available between native data types and XSD data types. Therefore, information can be lost during the translation, or the receiver is simply unable to do the mappings for certain native data types.
Unsigned numerical types, such as xsd:unsignedInt, xsd:unsignedLong, xsd:unsignedShort, and xsd:unsignedByte, are the typical examples. In .NET, the uint, ulong, ushort, and ubyte types map directly to those xsd types, but the Java language does not have unsigned numerical types. For interoperability, do not expose those numerical data types in the Web service methods. Instead, you can create wrapper methods to expose and transmit those numerical types as xsd:string (using System.Convert.ToString in C#).
For xsd:decimal, xsd:double, and xsd:float types, each platform might have different precision support. As a result, loss of precision might occur if you do not test the Web service after integration.
Whether a data type is a value type or a reference type, the communicating parties could also pose problems. The object of a value type is in the stack, but the object of a reference type is in the heap. That means a reference type can have a null pointer, but a
value type cannot have a null value. This can lead to a problem if the XSD type is mapped to a value type in one language but mapped to a reference type in another. For example, the xsd:dateTime is mapped to System.DateTime, which is a value type in C#. It is also mapped to java.util.Calendar, which is a reference type in Java. In fact, both java.util.Date and java.util.Calendar are reference types. In Java, it is a common practice to assign a null value to a reference type when it is not referencing any object. However, .NET Web services will throw a
System.FormatException if it receives a null value to its value type of data from a Java client. To avoid this problem, you can define a complex type to wrap the value type and set the complex type to be null to indicate a null reference.
In this article, you have seen some interoperability problems resulting from the use of certain data types. The general rules to achieve better interoperability in using data types are:
- Stick with the simple data types as much as possible. Totally avoid those fancy compex types such as
ArrayList,Tree, and even the commonHashtable. - Even though plain arrays are generally fine with interoperating Web services, be careful of what is in the arrays, make sure the elements in an array have the same meaning in both platforms, and avoid sending an array with null elements.
- Be conscious about how each platform implements some native primitive types such as
float,double, anddates and times.
In the next part of this series, I will explore the impact of namespace on the Web Service interoperability.
- Check out the other tips in this series:
- "Part 1: WSDL, RPC/encoded style, and WS-I conformance" (developerWorks, December 2004)
- "Part 3: Handling namespaces" (developerWorks, February 2005)
- Read pertinent specifications on WSDL, SOAP, and other standards:
- Gain guidance on determining which WSDL binding style/use is appropriate in the article "Which style of WSDL should I use?," by Russell Butek (developerWorks, October 2003).
- Study an overview of the architecture and functions of WS-I Test Tools in the article "Understanding the WS-I Test Tools," by Peter Brittenham (developerWorks, November 2003).
- See the tutorial "Using the WS-I Test Tools with Java technology," by Peter Brittenham for step-by-step instructions in using the Java versions of the WS-I test tools (developerWorks, November 2003).
- Learn more about the Web Services Interoperability Organization and IBM's commitment to interoperability.
- Want more? The developerWorks Web Services and SOA zone hosts hundreds of informative articles and introductory, intermediate, and advanced tutorials on how to develop Web services applications.
Wangming Ye is an IBM Certified Enterprise Developer and a Sun Certified Enterprise Architect for J2EE Technology. He began as a developer in the DCE/DFS department at Transarc Corporation (later merged into IBM), and then as one of the main developers of the WebSphere Content Distribution Framework in the WebSphere Edge Server group. He currently provides technical enablement for WebSphere business partners in the WebSphere Competency Center in the IBM Business Partner Technical Enablement organization. You can reach Wangming at yme@us.ibm.com.
Comments (Undergoing maintenance)





