Chapter 36. Using Wild Card Types

Abstract

There are instances when a schema author wants to defer binding elements or attributes to a defined type. For these cases, XML Schema provides three mechanisms for specifying wild card place holders. These are all mapped to Java in ways that preserve their XML Schema functionality.

36.1. Using Any Elements

Overview

The XML Schema any element is used to create a wild card place holder in complex type definitions. When an XML element is instantiated for an XML Schema any element, it can be any valid XML element. The any element does not place any restrictions on either the content or the name of the instantiated XML element.

For example, given the complex type defined in Example 36.1, “XML Schema Type Defined with an Any Element” you can instantiate either of the XML elements shown in Example 36.2, “XML Document with an Any Element”.

Example 36.1. XML Schema Type Defined with an Any Element

<element name="FlyBoy">
  <complexType>
    <sequence>
      <any />
      <element name="rank" type="xsd:int" />
    </sequence>
  </complexType>
</element>

Example 36.2. XML Document with an Any Element

<FlyBoy>
  <learJet>CL-215</learJet>
  <rank>2</rank>
</element>
<FlyBoy>
  <viper>Mark II</viper>
  <rank>1</rank>
</element>

XML Schema any elements are mapped to either a Java Object object or a Java org.w3c.dom.Element object.

Specifying in XML Schema

The any element can be used when defining sequence complex types and choice complex types. In most cases, the any element is an empty element. It can, however, take an annotation element as a child.

Table 36.1, “Attributes of the XML Schema Any Element” describes the any element’s attributes.

Table 36.1. Attributes of the XML Schema Any Element

AttributeDescription

namespace

Specifies the namespace of the elements that can be used to instantiate the element in an XML document. The valid values are:

##any
Specifies that elements from any namespace can be used. This is the default.
##other
Specifies that elements from any namespace other than the parent element’s namespace can be used.
##local
Specifies elements without a namespace must be used.
##targetNamespace
Specifies that elements from the parent element’s namespace must be used.
A space delimited list of URIs #\#local and \#\#targetNamespace
Specifies that elements from any of the listed namespaces can be used.

maxOccurs

Specifies the maximum number of times an instance of the element can appear in the parent element. The default value is 1. To specify that an instance of the element can appear an unlimited number of times, you can set the attribute’s value to unbounded.

minOccurs

Specifies the minimum number of times an instance of the element can appear in the parent element. The default value is 1.

processContents

Specifies how the element used to instantiate the any element should be validated. Valid values are:

strict
Specifies that the element must be validated against the proper schema. This is the default value.
lax
Specifies that the element should be validated against the proper schema. If it cannot be validated, no errors are thrown.
skip
Specifies that the element should not be validated.

Example 36.3, “Complex Type Defined with an Any Element” shows a complex type defined with an any element

Example 36.3. Complex Type Defined with an Any Element

<complexType name="surprisePackage">
  <sequence>
    <any processContents="lax" />
    <element name="to" type="xsd:string" />
    <element name="from" type="xsd:string" />
  </sequence>
</complexType>

Mapping to Java

XML Schema any elements result in the creation of a Java property named any. The property has associated getter and setter methods. The type of the resulting property depends on the value of the element’s processContents attribute. If the any element’s processContents attribute is set to skip, the element is mapped to a org.w3c.dom.Element object. For all other values of the processContents attribute an any element is mapped to a Java Object object.

The generated property is decorated with the @XmlAnyElement annotation. This annotation has an optional lax property that instructs the runtime what to do when marshaling the data. Its default value is false which instructs the runtime to automatically marshal the data into a org.w3c.dom.Element object. Setting lax to true instructs the runtime to attempt to marshal the data into JAXB types. When the any element’s processContents attribute is set to skip, the lax property is set to its default value. For all other values of the processContents attribute, lax is set to true.

Example 36.4, “Java Class with an Any Element” shows how the complex type defined in Example 36.3, “Complex Type Defined with an Any Element” is mapped to a Java class.

Example 36.4. Java Class with an Any Element

public class SurprisePackage {

    @XmlAnyElement(lax = true) protected Object any;
    @XmlElement(required = true)
    protected String to;
    @XmlElement(required = true)
    protected String from;

    public Object getAny() { return any; }

    public void setAny(Object value) { this.any = value; }

    public String getTo() {
        return to;
    }

    public void setTo(String value) {
        this.to = value;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String value) {
        this.from = value;
    }

}

Marshalling

If the Java property for an any element has its lax set to false, or the property is not specified, the runtime makes no attempt to parse the XML data into JAXB objects. The data is always stored in a DOM Element object.

If the Java property for an any element has its lax set to true, the runtime attempts to marshal the XML data into the appropriate JAXB objects. The runtime attempts to identify the proper JAXB classes using the following procedure:

  1. It checks the element tag of the XML element against the list of elements known to the runtime. If it finds a match, the runtime marshals the XML data into the proper JAXB class for the element.
  2. It checks the XML element’s xsi:type attribute. If it finds a match, the runtime marshals the XML element into the proper JAXB class for that type.
  3. If it cannot find a match it marshals the XML data into a DOM Element object.

Usually an application’s runtime knows about all of the types generated from the schema’s included in its contract. This includes the types defined in the contract’s wsdl:types element, any data types added to the contract through inclusion, and any types added to the contract through importing other schemas. You can also make the runtime aware of additional types using the @XmlSeeAlso annotation which is described in Section 32.4, “Adding Classes to the Runtime Marshaller”.

Unmarshalling

If the Java property for an any element has its lax set to false, or the property is not specified, the runtime will only accept DOM Element objects. Attempting to use any other type of object will result in a marshalling error.

If the Java property for an any element has its lax set to true, the runtime uses its internal map between Java data types and the XML Schema constructs they represent to determine the XML structure to write to the wire. If the runtime knows the class and can map it to an XML Schema construct, it writes out the data and inserts an xsi:type attribute to identify the type of data the element contains.

If the runtime cannot map the Java object to a known XML Schema construct, it will throw a marshaling exception. You can add types to the runtime’s map using the @XmlSeeAlso annotation which is described in Section 32.4, “Adding Classes to the Runtime Marshaller”.

36.2. Using the XML Schema anyType Type

Overview

The XML Schema type xsd:anyType is the root type for all XML Schema types. All of the primitives are derivatives of this type, as are all user defined complex types. As a result, elements defined as being of xsd:anyType can contain data in the form of any of the XML Schema primitives as well as any complex type defined in a schema document.

In Java the closest matching type is the Object class. It is the class from which all other Java classes are sub-typed.

Using in XML Schema

You use the xsd:anyType type as you would any other XML Schema complex type. It can be used as the value of an element element’s type element. It can also be used as the base type from which other types are defined.

Example 36.5, “Complex Type with a Wild Card Element” shows an example of a complex type that contains an element of type xsd:anyType.

Example 36.5. Complex Type with a Wild Card Element

<complexType name="wildStar">
  <sequence>
    <element name="name" type="xsd:string" />
    <element name="ship" type="xsd:anyType" />
  </sequence>
</complexType>

Mapping to Java

Elements that are of type xsd:anyType are mapped to Object objects. Example 36.6, “Java Representation of a Wild Card Element” shows the mapping of Example 36.5, “Complex Type with a Wild Card Element” to a Java class.

Example 36.6. Java Representation of a Wild Card Element

public class WildStar {

    @XmlElement(required = true)
    protected String name;
    @XmlElement(required = true) protected Object ship;

    public String getName() {
        return name;
    }

    public void setName(String value) {
        this.name = value;
    }

    public Object getShip() { return ship; }

    public void setShip(Object value) { this.ship = value; }
}

This mapping allows you to place any data into the property representing the wild card element. The Apache CXF runtime handles the marshaling and unmarshaling of the data into usable Java representation.

Marshalling

When Apache CXF marshals XML data into Java types, it attempts to marshal anyType elements into known JAXB objects. To determine if it is possible to marshal an anyType element into a JAXB generated object, the runtime inspects the element’s xsi:type attribute to determine the actual type used to construct the data in the element. If the xsi:type attribute is not present, the runtime attempts to identify the element’s actual data type by introspection. If the element’s actual data type is determined to be one of the types known by the application’s JAXB context, the element is marshaled into a JAXB object of the proper type.

If the runtime cannot determine the actual data type of the element, or the actual data type of the element is not a known type, the runtime marshals the content into a org.w3c.dom.Element object. You will then need to work with the element’s content using the DOM APis.

An application’s runtime usually knows about all of the types generated from the schema’s included in its contract. This includes the types defined in the contract’s wsdl:types element, any data types added to the contract through inclusion, and any types added to the contract through importing other schema documents. You can also make the runtime aware of additional types using the @XmlSeeAlso annotation which is described in Section 32.4, “Adding Classes to the Runtime Marshaller”.

Unmarshalling

When Apache CXF unmarshals Java types into XML data, it uses an internal map between Java data types and the XML Schema constructs they represent to determine the XML structure to write to the wire. If the runtime knows the class and can map the class to an XML Schema construct, it writes out the data and inserts an xsi:type attribute to identify the type of data the element contains. If the data is stored in a org.w3c.dom.Element object, the runtime writes the XML structure represented by the object but it does not include an xsi:type attribute.

If the runtime cannot map the Java object to a known XML Schema construct, it throws a marshaling exception. You can add types to the runtime’s map using the @XmlSeeAlso annotation which is described in Section 32.4, “Adding Classes to the Runtime Marshaller”.

36.3. Using Unbound Attributes

Overview

XML Schema has a mechanism that allows you to leave a place holder for an arbitrary attribute in a complex type definition. Using this mechanism, you can define a complex type that can have any attribute. For example, you can create a type that defines the elements <robot name="epsilon" />, <robot age="10000" />, or <robot type="weevil" /> without specifying the three attributes. This can be particularly useful when flexibility in your data is required.

Defining in XML Schema

Undeclared attributes are defined in XML Schema using the anyAttribute element. It can be used wherever an attribute element can be used. The anyAttribute element has no attributes, as shown in Example 36.7, “Complex Type with an Undeclared Attribute”.

Example 36.7. Complex Type with an Undeclared Attribute

<complexType name="arbitter">
  <sequence>
    <element name="name" type="xsd:string" />
    <element name="rate" type="xsd:float" />
  </sequence>
  <anyAttribute />
</complexType>

The defined type, arbitter, has two elements and can have one attribute of any type. The elements three elements shown in Example 36.8, “Examples of Elements Defined with a Wild Card Attribute” can all be generated from the complex type arbitter.

Example 36.8. Examples of Elements Defined with a Wild Card Attribute

<officer rank="12"><name>...</name><rate>...</rate></officer>
<lawyer type="divorce"><name>...</name><rate>...</rate></lawyer>
<judge><name>...</name><rate>...</rate></judge>

Mapping to Java

When a complex type containing an anyAttribute element is mapped to Java, the code generator adds a member called otherAttributes to the generated class. otherAttributes is of type java.util.Map<QName, String> and it has a getter method that returns a live instance of the map. Because the map returned from the getter is live, any modifications to the map are automatically applied. Example 36.9, “Class for a Complex Type with an Undeclared Attribute” shows the class generated for the complex type defined in Example 36.7, “Complex Type with an Undeclared Attribute”.

Example 36.9. Class for a Complex Type with an Undeclared Attribute

public class Arbitter {

    @XmlElement(required = true)
    protected String name;
    protected float rate;

    @XmlAnyAttribute private Map<QName, String> otherAttributes = new HashMap<QName, String>();

    public String getName() {
        return name;
    }

    public void setName(String value) {
        this.name = value;
    }

    public float getRate() {
        return rate;
    }

    public void setRate(float value) {
        this.rate = value;
    }

    public Map<QName, String> getOtherAttributes() { return otherAttributes; }

}

Working with undeclared attributes

The otherAttributes member of the generated class expects to be populated with a Map object. The map is keyed using QNames. Once you get the map , you can access any attributes set on the object and set new attributes on the object.

Example 36.10, “Working with Undeclared Attributes” shows sample code for working with undeclared attributes.

Example 36.10. Working with Undeclared Attributes

Arbitter judge = new Arbitter();
Map<QName, String> otherAtts = judge.getOtherAttributes();

QName at1 = new QName("test.apache.org", "house");
QName at2 = new QName("test.apache.org", "veteran");

otherAtts.put(at1, "Cape");
otherAtts.put(at2, "false");

String vetStatus = otherAtts.get(at2);

The code in Example 36.10, “Working with Undeclared Attributes” does the following:

Gets the map containing the undeclared attributes.

Creates QNames to work with the attributes.

Sets the values for the attributes into the map.

Retrieves the value for one of the attributes.