Contents


Web services hints and tips: avoid anonymous types

Comments

We often see Web service XML schemas written using anonymous types. It is unlikely that the authors of such schemas realize the potential problems that they may cause when they build such schemas. This article codifies issues that may arise from using anonymous types in hopes that readers will avoid them.

What is an anonymous type?

An anonymous XML type is one which is embedded within an xsd:element. Since it is embedded within an xsd:element, it is not named. For example, see listing 1.

Listing 1. Anonymous 'person' type
<xsd:element name="person">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="first" type="xsd:string"/>
      <xsd:element name="last" type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

The complexType within the element named "person" has no name itself, so it is anonymous. You can convert this anonymous type into a named type by following these simple steps:

  • Move the complexType definition out to the global scope.
  • Give the complexType a name attribute.
  • Give the global element a type attribute which refers to the newly named complexType.

We followed these steps to transform listing 1's anonymous type into listing 2's named type.

Listing 2. Named Person type
<xsd:element name="person" type="Person"/>
<xsd:complexType name="Person">
  <xsd:sequence>
    <xsd:element name="first" type="xsd:string"/>
    <xsd:element name="last" type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>

The schemas in listing 1 and listing 2 are logically identical. Either one of them can be used to validate the XML instance shown in listing 3.

Listing 3. Named Person type
<person>
     <first>John</first>
     <last>Doe</last>
</person>

What's wrong with anonymous types?

We will use the schemas in listings 4 and 5 throughout the rest of this discussion. Listing 4 does not define a single named type. All types are anonymous. For comparison, listing 5 shows the equivalent schema with named types. We applied the steps above to listing 4 to create listing 5.

Listing 4. The account element defined with anonymous types
<xsd:element name="account">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="number" type="xsd:int"/>
      <xsd:element name="person">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="firstName" type="xsd:string"/>
            <xsd:element name="lastName" type="xsd:string"/>
            <xsd:element name="otherNamesOnAccount" minOccurs="0" maxOccurs="unbounded">
              <xsd:complexType>
                <xsd:sequence>
                  <xsd:element name="person">
                    <xsd:complexType>
                      <xsd:sequence>
                        <xsd:element name="firstName" type="xsd:string"/>
                        <xsd:element name="lastName" type="xsd:string"/>
                      </xsd:sequence>
                    </xsd:complexTypeb>
                  </xsd:element>
                  <xsd:element name="relationshipToAccountOwner" type="xsd:string"/>
                </xsd:sequence>
              </xsd:complexType>
            </xsd:element>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>
Listing 5. The account element defined with named types
<xsd:element name="account" type="tns:Account"/>

<xsd:complexType name="Account">
  <xsd:sequence>
    <xsd:element name="number" type="xsd:int"/>
    <xsd:element name="person" type="tns:AccountOwner"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="AccountOwner">
  <xsd:sequence>
    <xsd:element name="firstName" type="xsd:string"/>
    <xsd:element name="lastName" type="xsd:string"/>
    <xsd:element name="otherNamesOnAccount" minOccurs="0" maxOccurs="unbounded"
        type="tns:OtherNameOnAccount"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="OtherNameOnAccount">
  <xsd:sequence>
    <xsd:element name="person" type="tns:Person"/>
    <xsd:element name="relationshipToAccountOwner" type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="Person">
  <xsd:sequence>
    <xsd:element name="firstName" type="xsd:string"/>
    <xsd:element name="lastName" type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>

Why do we dislike the anonymous types in listing 4?

  • There is no re-use
  • Anonymous types still often must be named
  • Listing 4 is not as elegant as listing 5

The rest of this article will go into details on each of these three points.

No re-use

This is our primary issue with anonymous types. When a type is defined anonymously within an element, only that element can be of that type. If you wish another element elsewhere to be the same type, the best you can do is to cut/paste the anonymous type definition into the new element.

For instance, the firstName/lastName pair of elements is duplicated in listing 4 – one set within the outer person element and the other within the inner person element. To enable re-use, these two elements should be placed into their own named type. Without such a named type, the elements were simply copied from one point of the schema to another.

Note that in listing 5 we now have such a named type: Person. Listing 5 is the named equivalent to listing 4; we did not make any changes that would affect the structure of XML instances. But now that we have a named type for Person, we could further improve the schema in listing 5 by re-using the Person type within AccountOwner.

Anonymous types still often must be named

Even if you do not care about re-use, there is still a potential hazard to using anonymous types. Consider how an XML schema is used. For instance, in a Web service definition, there is a good chance that the XML schema will map to a programming language. If so, then within that language, types likely can no longer be anonymous. They must be named somehow, and the name must be derived from the surrounding schema. No matter what rules you come up with for such a derivation, there will likely exist some scenario which breaks those rules.

For example, the JAXB specification’s rules for mapping XML schema to Java breaks down with the schema defined in listing 4. JAXB defines that an anonymous type’s class takes on a name derived from the containing element. Moreover, embedded anonymous complex types in XML become inner classes in Java. According to JAXB, the XML schema in listing 4 will map to the following Java classes:

  • Account
  • Account.Person
  • Account.Person.OtherNamesOnAccount
  • Account.Person.OtherNamesOnAccount.Person

Java does not allow nested classes to share names with containing classes. You will have a compile-time error something like: "The nested type Person cannot hide an enclosing type".

Our solution to this problem is to name all types as we did in listing 5. You can see that we mostly applied the name derivation rules that JAXB uses – deriving the name of the Java type from the name of the enclosing element. But we did not do it for all types, otherwise we would have ended up with two "Person" types. So we arbitrarily renamed the first "Person" type to "AccountOwner".

The JAXB binding file

While we prefer our solution, that solution requires you to change the XML schema. For those cases where you cannot change the schema, JAXB provides another solution. JAXB allows a user to write a binding file which tells the JAXB engine how to map XML to Java. The authors of the JAXB specification knew that a specified mapping would not always work for everyone in every situation, so they provided this mechanism to deviate from the standard. Listing 6 is the binding file which will tell the JAXB generator to modify the class generated from the anonymous complexType under the first person element so that it is named "AccountOwner".

Listing 6. A JAXB binding file needed to fix the issue with listing 4
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xsi="htp://www.w3.org/2001/XMLSchema-instance" 
mlns:xsd="http://www.w3.org/2001/XMLSchema"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb 
http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
		jaxb:version="2.1">
  <jaxb:bindings schemaLocation="Anonymous.xsd" node="/xsd:schema
    /xsd:element[@name='account']/xsd:complexType/xsd:sequence
    /xsd:element[@name='person']/xsd:complexType">
    <jaxb:class name="AccountOwner"/>
  </jaxb:bindings>
</jaxb:bindings>

The inner jaxb:bindings element contains a schemaLocation attribute to tell the engine which schema this binding file affects; and it contains a node attribute whose value is an XPath expression for the element for which you want to apply the special binding. The jaxb:class element then tells you that the JAXB class generated from the given XPath’s element will be named "AccountOwner". For more information on the JAXB mapping file, see the Resources section.

JAXB is robust enough to allow you to name anonymous types as you need to via the binding file. But it adds complexity to the process of building your service. And you cannot depend on all language mappings to give you this same level of functionality. So if you have the freedom to modify the schemas, it is best to avoid anonymous types.

Not as elegant

Our final complaint about listing 4 is that it is not as elegant as listing 5. We freely admit that this last reason is purely a matter of opinion. But in our opinion, anonymous types aren’t as readable as named types; a deeply nested XML structure is almost always more difficult to understand readily. Structures of anonymous types are more difficult to discuss than structures of named types.

Summary

Anonymous types should be avoided for a number of reasons:

  • they do not allow re-use;
  • they may cause problems when a mapping must name anonymous types;
  • they are not as elegant as named types.

These issues can be remedied by converting anonymous types to named types. In the case of JAXB's mapping from XML schemas to Java, if you are not able to change the schema, you can provide a JAXB binding file to the JAXB engine to alleviate any problems you may encounter.


Downloadable resources


Related topics


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and web services
ArticleID=754620
ArticleTitle=Web services hints and tips: avoid anonymous types
publish-date=08302011