When writing web services it’s quite common to need to represent an array of some custom type.
For this example I’m working with a “Box” that contains an array of “Thing” elements (it’ll also contain a “Colour” element, for reasons that will become clear).
Each “Thing” element has to have an attribute (“name”).
There are two basic ways of doing this in XML:
| Bare: | Wrapped: |
<Box> <Thing name="Thing 1"/> <Thing name="Thing 2"/> </Box> |
<Box>
<Things>
<Thing name="Thing 1"/>
<Thing name="Thing 2"/>
</Things>
</Box>
|
When we prepare the schema for representing these structures we have two options: use an inline complex type or use a top level named complex type.
| Inline: | Global: |
<xsd:element name="Box">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Thing" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="name" type="xsd:token"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
|
<xsd:complexType name="BoxType">
<xsd:attribute name="name" type="xsd:token"/>
</xsd:complexType>
...
<xsd:element name="Box">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Thing" type="BoxType" minOccurs="0" maxOccurs="unbounded"></xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
|
So we have a total of four options for what to put in our schema.
The question is, which is best?
For my purposes the answer is based on which gives the best representation as XML and when converted into java (by wsdl2java) or c# (by wsdl.exe).
What do I want to see?
Well, what I don’t want are things like this:
List<Things> things = Box.getThings(); |
I have a list of Thing objects, not Things objects. |
List<Thing> things = Box.getThings().getThing(); |
Too verbose, and getThings() may return null, which makes it even more verbose. |
List<Thing> things = Box.getThing(); |
getThing() is singular, why is it returning a list of things?. |
<Box> <Things name="Thing 1"/> <Things name="Thing 2"/> </Box> |
Thing 1 is a Thing, not a Things. |
Although the examples above are java, I have the same requirements from C#.
So, four options, and three things to look at for each one.
Bare, Inline complex type
The schema:
<xsd:complexType name="TricksResponseType">
<xsd:sequence>
<xsd:element name="Box">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Colour" type="xsd:token"></xsd:element>
<xsd:element name="Thing" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="name" type="xsd:token"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
The XML:
<cat:TricksResponse>
<Box>
<Colour>?</Colour>
<Thing name="Thing 1"/>
<Thing name="Thing 2"/>
</Box>
</cat:TricksResponse>
Java usage:
{
TricksResponseType response = hat.getTricks( null );
Box box = response.getBox();
String colour = box.getColour();
List< Thing > things = box.getThing();
}
C# usage
{
TricksResponseType response = svc.GetTricks( null );
TricksResponseTypeBox box = response.Box;
string colour = box.Colour;
TricksResponseTypeBoxThing[] things = box.Thing;
}
What’s wrong with that?
Well the XML representation is as good as we can get, it’s concise (it doesn’t need a pointless wrapper) and the elements have a nice singular name.
The downside is that the properties in java and C# are singular, and they shouldn’t be.
Bare, Global complex type
The schema:
<xsd:complexType name="ThingType">
<xsd:attribute name="name" type="xsd:token"/>
</xsd:complexType>
<xsd:complexType name="TricksResponseType">
<xsd:sequence>
<xsd:element name="Box">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Colour" type="xsd:token"></xsd:element>
<xsd:element name="Things" type="tns:ThingType" minOccurs="0" maxOccurs="unbounded"></xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
The XML:
<cat:TricksResponse>
<Box>
<Colour>?</Colour>
<Things name="Thing 1"/>
<Things name="Thing 2"/>
</Box>
</cat:TricksResponse>
Java usage:
{
TricksResponseType response = hat.getTricks( null );
Box box = response.getBox();
String colour = box.getColour();
List< ThingType > things = box.getThings();
}
C# usage
{
TricksResponseType response = svc.GetTricks( null );
TricksResponseTypeBox box = response.Box;
string colour = box.Colour;
ThingType[] things = box.Things;
}
Well that’s tidied up the java and C# representation, but made a total mess of the XML.
Notice that the name of the customType is seen in both java and C#, but is not seen at all in the XML itself.
Wrapped, Inline complex type
The schema:
<xsd:complexType name="TricksResponseType">
<xsd:sequence>
<xsd:element name="Box">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Colour" type="xsd:token"></xsd:element>
<xsd:element name="Things" minOccurs="1" maxOccurs="1">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Thing" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="name" type="xsd:token"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
The XML:
<cat:TricksResponse>
<Box>
<Colour>?</Colour>
<Things>
<Thing name="Thing 1"/>
<Thing name="Thing 2"/>
</Things>
</Box>
</cat:TricksResponse>
Java usage:
{
TricksResponseType response = hat.getTricks( null );
Box box = response.getBox();
String colour = box.getColour();
Things things = box.getThings();
List< Thing > thingList = things.getThing();
}
C# usage
{
TricksResponseType response = svc.GetTricks( null );
TricksResponseTypeBox box = response.Box;
string colour = box.Colour;
TricksResponseTypeBoxThing[] things = box.Things;
}
Well now this is interesting – the XML has gained an unnecessary wrapper, but maybe I can live with that if it makes the generated code look good.
But see how the C# code is almost perfect (the type name is messy, but the pluralities are perfect) whilst the java code is horrid.
This bonus for the C# is because the .net wsdl.exe code generator short circuits elements that only have one child element.
Worth noting that C# has to generate a nasty name for the ‘Thing’ type, whilst java (with ubiquitus namespaces) is able to provide a much nicer name.
Wrapped, Global complex type
The schema:
<xsd:complexType name="ThingType">
<xsd:attribute name="name" type="xsd:token"/>
</xsd:complexType>
<xsd:complexType name="TricksResponseType">
<xsd:sequence>
<xsd:element name="Box">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Colour" type="xsd:token"></xsd:element>
<xsd:element name="Things" minOccurs="1" maxOccurs="1">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Thing" type="tns:ThingType" minOccurs="0" maxOccurs="unbounded"></xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
The XML:
<cat:TricksResponse>
<Box>
<Colour>?</Colour>
<Things>
<Thing name="Thing 1"/>
<Thing name="Thing 2"/>
</Things>
</Box>
</cat:TricksResponse>
Java usage:
{
TricksResponseType response = hat.getTricks( null );
Box box = response.getBox();
String colour = box.getColour();
Things things = box.getThings();
List< ThingType > thingList = things.getThing();
}
C# usage
{
TricksResponseType response = svc.GetTricks( null );
TricksResponseTypeBox box = response.Box;
string colour = box.Colour;
ThingType[] things = box.Things;
}
This structure provides a perfect solution for the .net users, but the java users are still lumbered with the intermediate object.
Conclusions
The first conclusion is that the use of wrapper objects, handled so nicely in C#, is really ugly in java so I recommend against using it at all.
The benefit of having control over the name makes global complex types nicer to work with in both C# and java.
But this leaves a problem with the plurality, it’s going to end up being wrong in either the XML or the code.
I think it’s more important to be right in the XML, so I’ll be using the schema from my first example.
In case you think the C# wsdl tool is much better than the java one from this please bear in mind that wsdl2java can work with abstract wsdl files, whereas the C# equivalent generates nothing unless the WSDL file has a concrete service defined – which is rubbish.