IBM®
Skip to main content
    Country/region [select]      Terms of use
 
 
      
     Home      Products      Services & solutions      Support & downloads      My account     

developerWorks > Web services
developerWorks
Web Services Programming Tips and Tricks: Array Gotcha - Null Array vs. Empty Array
e-mail it!
Contents:
An XML array
The problem
Is there a way around this issue?
Summary
Resources
About the authors
Rate this article
Subscriptions:
dW newsletters
dW Subscription
(CDs and downloads)
An XML schema pattern to help distinguish between a null and an empty array

Level: Introductory

<%@ page import="java.util.*, java.text.*, com.ibm.developerworks.*, DataConnectionBean" %> <% // for performance enhancements as the user explores dW, this JSP stores a DataConnectionBean and a RatingsResultsBean // in the session. This is also done in the servlet, but the majority of users won't make it there (rate an article). DataConnectionBean dataConnectionBean = null; RatingsResultsBean ratingsResultsBean = null; RatingsResultsDataHelper ratingsResultsDataHelper = null; Hashtable ratingsResultsBeanHashtable = null; Long article_id = new Long(-1); String article_id_string = request.getParameter("article_id"); if (article_id_string != null && !(article_id_string.trim().equals(""))) { article_id_string = DataHelper.replaceString(article_id_string, "-", ""); article_id_string = DataHelper.removeFirstString(article_id_string, "0"); article_id = new Long(article_id_string); } // try to get a ratings results bean from the hashtable in the session before going to the DB ratingsResultsBeanHashtable = (Hashtable)session.getValue(RatingsConstants.SESSION_RATINGS_RESULTS_BEAN_HASHTABLE); // try to get the bean from the hashtable, if it's not null. If it is, create a new results bean hashtable if (ratingsResultsBeanHashtable != null && article_id != null) ratingsResultsBean = (RatingsResultsBean)ratingsResultsBeanHashtable.get(article_id); else if (ratingsResultsBeanHashtable == null) ratingsResultsBeanHashtable = new Hashtable(); // no results bean in the session, create one if (ratingsResultsBean == null) { //get an existing data connection bean if there is one dataConnectionBean = (DataConnectionBean)session.getValue(RatingsConstants.SESSION_CONNECTION_BEAN); if (dataConnectionBean == null) { dataConnectionBean = new DataConnectionBean(); //dataConnectionBean.load("/usr/IBMWebAS/properties/dwRatings.properties"); //can/should we use the poll prop file dataConnectionBean.load("/was4/WebSphere/AppServer/installedApps/dWApp.ear/dWApp.war/dwRatings.properties"); //can/should we use the poll prop file if (dataConnectionBean != null) session.putValue(RatingsConstants.SESSION_CONNECTION_BEAN, dataConnectionBean); } ratingsResultsDataHelper = new RatingsResultsDataHelper(dataConnectionBean); ratingsResultsBean = ratingsResultsDataHelper.getRatingsResultsBean(article_id.longValue()); } %> <% if (ratingsResultsBean != null) { // store the ratingsresultsbean in the session, which is also done in the controlling servlet, but they probably won't // make it there if (ratingsResultsBeanHashtable.containsKey(article_id)) //ratings results bean already exists for this page in the session { ratingsResultsBeanHashtable.remove(article_id); ratingsResultsBeanHashtable.put(article_id, ratingsResultsBean); } else { ratingsResultsBeanHashtable.put(article_id, ratingsResultsBean); } session.putValue(RatingsConstants.SESSION_RATINGS_RESULTS_BEAN_HASHTABLE, ratingsResultsBeanHashtable); if (ratingsResultsBean.getNumberOfRatings() >= 10) { %> Average rating: (<%= ratingsResultsBean.getNumberOfRatings() %> ratings) <% } } //else, do nothing - don't try to display a rating if we don't have a bean!! %>

Russell Butek, Software Engineer, IBM
Richard Scheuerle, Jr., Software Engineer, IBM

6 January 2004

Some programs depend on a distinction between a null array and an empty array. What is often used to represent arrays in XML schemas does not have any such distinction. Is there anything you can do to get around this 'feature' of XML?

When working with Web services, we all too often assume anything we can do in a programming language we can do in XML. There are many cases where that is not true. This tip addresses one of those cases: the distinction between an array which is null, and an array which has no elements.

An XML array
Most programming languages, like Java, have the concept of an array: a sequential collection of like elements. XML also has a sequential collection of like elements: an XML schema element with a maxOccurs attribute whose value is greater than 1. So it stands to reason that Java's sequential collection of like elements would map nicely to XML's sequential collection of like elements. Listing 1 defines a complexType which contains such an XML schema 'array'.

Listing 1. A complexType containing an 'array'

<complexType name="bean">
  <sequence>
    <element name="name" type="xsd:string"/>
    <element name="array" minOccurs="0" maxOccurs="unbounded"
        nillable="true" type="xsd:int"/>
  </sequence>
</complexType>

The problem
This XML 'array' is not strictly an array. It is an element with an "occurrence constraint", which means that the element is defined to occur a specific number of times, in this case 0 or more times. This does sound a lot like an array, and for most intents and purposes, it is. But the mapping isn't perfect. You should be aware of the shortcomings so they don't catch you unawares.

Following the JAX-RPC mapping rules, the complexType in Listing 1 would become the Java bean in Listing 2 (actually, the bean would have getters and setters, but we'll keep it simple for this discussion).

Listing 2. A bean containing an array.

public class Bean {
    public java.lang.String name;
    public java.lang.Integer[] array;
}

(Note that Bean's array variable is an array of java.lang.Integer, not an array of int. The array element from the XML schema is nillable. A Java int cannot be null. A java.lang.Integer can be null. So we use java.lang.Integer in this mapping.)

Table 1 shows a number of examples of mapping an instance of the Java Bean to an instance of the corresponding XML. The first row is the Java representation; the second row is the corresponding XML representation.

Table 1. Array examples
Two-element arrayOne-element array w/ null valueEmpty arrayNull array
bean.name="2-el"
bean.array={5, 10}
bean.name="1-el"
bean.array={null}
bean.name="empty"
bean.array={}
bean.name="null"
bean.array=null

<bean>
  <name>2-el</name>
  <array>5</array>
  <array>10</array>
</bean>

<bean>
  <name>1-el</name>
  <array xsi:nil="true"/>
</bean>

<bean>
  <name>empty</name>
</bean>

<bean>
  <name>null</name>
</bean>

One obvious thing to note about Table 1 - and it's the topic of this tip - is that an empty array and a null array in Java map to the same XML instance. This is not good if you're depending on a distinction between the two!

One easy trap to fall into here is to guess that a null array inside a bean is really represented by the XML in the second column. But as we hope we've shown in the table, that really represents an array with a single element whose value is null, not a null array.

Is there a way around this issue?
Of course! The thing to be aware of is that an array in most programming languages is really made up of two things: there are the contents of the array; and there is the array itself - a wrapper, if you like, around the contents. An XML 'array' is only a list of the elements. There is no wrapper.

So the solution is simple: create a wrapper for the array, as shown in Listing 3.

Listing 3. A bean containing a wrappered array.
<complexType name="arrayWrapper">
  <sequence>
    <element name="el" nillable="true" maxOccurs="unbounded" minOccurs="0" type="xsd:int"/>
  </sequence>
</complexType>
<complexType name="bean">
  <sequence>
    <element name="name" type="xsd:string"/>
    <element name="array" nillable="true" type="tns:arrayWrapper"/>
  </sequence>
</complexType>

Table 2 is the array example table for this XML schema.

Table 2. Array examples for a wrapped array
Two-element arrayOne-element array w/ null valueEmpty arrayNull array
bean.name="2-el"
bean.array={5, 10}
bean.name="1-el"
bean.array={null}
bean.name="empty"
bean.array={}
bean.name="null"
bean.array=null

<bean>
  <name>2-el</name>
  <array>
    <el>5</el>
    <el>10</el>
  <array>
</bean>

<bean>
  <name>1-el</name>
  <array>
    <el xsi:nil="true"/>
  <array>
</bean>

<bean>
  <name>empty</name>
  <array>
  <array>
</bean>

<bean>
  <name>null</name>
  <array xsi:nil="true"/>
</bean>

As you can see, the empty instance and the null instance of the arrayWrapper complexType are distinct from each other.

Mapping of wrapped arrays
WebSphere 6.0 will recognize this special wrapped array pattern. Instead of generating the extra Java bean, it will simply generate what you see in Listing 2. Microsoft's .NET also recognizes this wrapped array pattern and generates friendly C# code.

This solution isn't a cure-all. First of all, it's rather more complex than a simple minOccurs/maxOccurs representation of an array. Secondly, instead of a simple bean containing an array, this XML schema really looks like a bean containing a bean containing an array; and that's likely what you'll end up with if you map this XML schema into Java with your favorite JAX-RPC WSDL-to-Java tool. Until standards bodies recognize and map this wrapped array pattern appropriately, this solution is something you should apply only if you really must distinguish a null array from an empty array.

Summary
XML does not distinguish between a null array and an empty array. There is an XML schema pattern that you can follow to get the equivalent distinction, but this pattern is not well recognized by standards bodies and should only be used when absolutely necessary.

Resources

About the authors
Russell Butek is one of the developers of IBM's WebSphere Web services engine. He is also IBM's representative on the JAX-RPC Java Specification Request (JSR) expert group. He was involved in the implementation of Apache's AXIS SOAP engine, driving AXIS 1.0 to comply with JAX-RPC 1.0. Previously, he was a developer of IBM's CORBA ORB and an IBM representative on a number of OMG task forces: the portable interceptor task force (of which he was chair), the core task force, and the interoperability task force. You can contact Russell at butek at us.ibm.com.


Richard Scheuerle is one of the developers of IBM's WebSphere Web Services engine. He was involved in the implementation of Apache's AXIS SOAP engine. Richard has 10 years of development experience with compiler/language design. Currently he is concentrating on the Web service engine performance and API design. Richard has also worked on the development of CORBA tooling and chip validation software. You can contact Richard at scheu at us.ibm.com.



e-mail it!

What do you think of this document?
Killer! (5)Good stuff (4)So-so; not bad (3)Needs work (2)Lame! (1)

Comments?



developerWorks > Web services
developerWorks
  About IBM  |  Privacy  |  Terms of use  |  Contact