Skip to main content

Web services programming tips and tricks: Improve the interoperability between J2EE and .NET, Part 2

Managing collections, arrays, or even primitive data types

Wangming Ye (yme@us.ibm.com), Software Engineer, IBM, Software Group
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.

Summary:  Part 2 of this series explores the source of common interoperability challenges facing Web services integration across platforms. Follow along as Wangming Ye analyzes the interop failures resulting from the use of certain data types and ways to overcome them such as collections, arrays, or primitive data types.

Date:  21 Jan 2005
Level:  Intermediate
Activity:  6262 views

Introduction

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.Hashtable
  • Vectors
  • Hashmap
  • Set
  • ArrayList

While in C#, there are:

  • System.Collections.Hashtable
  • SortedList
  • Queue
  • Stack
  • ArrayList

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.


An array with null elements

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.


Conclusion

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 common Hashtable.
  • 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, and dates and times.

In the next part of this series, I will explore the impact of namespace on the Web Service interoperability.


Resources

About the author

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)



Trademarks  |  My developerWorks terms and conditions

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, XML
ArticleID=33296
ArticleTitle=Web services programming tips and tricks: Improve the interoperability between J2EE and .NET, Part 2
publish-date=01212005
author1-email=yme@us.ibm.com
author1-email-cc=

My developerWorks community

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.

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).

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).

Rate a product. Write a review.

Special offers