A complex type describes text that may contain markup in the form of elements and attributes. A complex type can be assigned to elements but not to attributes, whose type is always simple.
The definition of complex types may contain declarations for elements and attributes or references to those declarations. If a complex type defines both element and attribute declarations, then the attribute declarations must follow the element ones. An element declaration has the following form:
<xs:element name="elementName" type="elementType"/>
where elementName is a name for the element and elementType is either a simple or a complex type. It is common to declare an element once and then refer to it inside a type definition as follows:
<xs:element ref="elementReference"/>
where elementReference is the name of an element declared elsewhere. The element element has optional attributes minOccurs and maxOccurs that indicate the minimum and maximum number of times the element must repeat. The default value for both attributes is 1. The attribute maxOccurs can be set to unbounded to repeat the element indefinitely. For instance, by setting minOccurs to 0 the corresponding element becomes optional.
An attribute declaration has the following form:<xs:attribute name="attributeName" type="attributeType"/>
where attributeName is a name for the attribute and attributeType is a simple type. As for elements, it is common to declare an attribute once and then to refer to it as follows:
<xs:attribute ref="attributeReference"/>
where attributeReference is the name of an attribute declared elsewhere. The attribute element has an optional attribute use whose values are optional (default) and required with the obvious meaning.
Element and attribute declarations can be used to build complex types. The simplest example of a complex type is the empty type that describes an element with no content and no attributes:
<xs:complexType name="emptyType"/>
A slightly more complicated example is that of an element with empty content but having attributes. Here is an example:
<xs:complexType name="bookType"> <xs:attribute name="price" type="xs:decimal"/> </xs:complexType>
The type bookType describes an element with empty content and a single attribute price of type decimal.
Increasing the complexity, we may have an element with simple content (character data with no markup) and attributes. The syntax to describe this case is somewhat complicated:
<xs:complexType name="bookType"> <xs:simpleContent> <xs:extension base="xs:string"> <xs:attribute name="price" type="xs:decimal"/> </xs:extension> </xs:simpleContent> </xs:complexType>
The defined type is an extension of the primitive type string by adding an attribute price of type decimal.
Now that we have solved the base cases, we can move to the structured ones, that is complex types that define elements containing subelements and possibly attributes as well. We can build complex types using the following constructors:
<xs:element name="street" type="xs:string"/> <xs:element name="number" type="xs:integer"/> <xs:element name="city" type="xs:string"/> <xs:element name="state" type="xs:string"/> <xs:element name="address" type="addressType"/> <xs:complexType name="addressType"> <xs:sequence> <xs:element ref="street"/> <xs:element ref="number"/> <xs:element ref="city"/> <xs:element ref="state"/> </xs:sequence> </xs:complexType>The element address must contain the subelements street, number, city and state in this order.
<xs:element name="email" type="xs:string"/> <xs:element name="phone" type="xs:string"/> <xs:element name="contact" type="contactType"/> <xs:complexType name="contactType"> <xs:choice> <xs:element ref="email"/> <xs:element ref="phone"/> </xs:choice> </xs:complexType>
<xs:element name="email" type="xs:string"/> <xs:element name="phone" type="xs:string"/> <xs:element name="contact" type="contactType"/> <xs:complexType name="contactType"> <xs:all> <xs:element ref="email"/> <xs:element ref="phone"/> </xs:all> </xs:complexType>In this case, the contact element must contain both email and phone subelements in any order. A few restrictions apply to the all construct: it can only contain element references or declarations. The maximum number of occurrences of each contained element must be 1. Moreover, sequence and choice operators cannot contain the all operator.
<xs:complexType name="unstructuredType"> <xs:sequence> <xs:any minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType>The any element has an optional attribute namespace that permits to specify the namespace to match its content. Any element belonging to the specified namespace is accepted. Moreover, it has an optional attribute processContents with the following possible values:
<xs:complexType name="htmlType"> <xs:sequence> <xs:any namespace="http://www.w3.org/1999/xhtml" minOccurs="0" maxOccurs="unbounded" processContents="skip"/> </xs:sequence> </xs:complexType>The following note element is valid with respect to the defined htmlType:
<note xmlns:x="http://www.w3.org/1999/xhtml"> <x:p> <x:em>Scott Means</x:em> is the author of an excellent book on <x:strong>XML</x:strong>. </x:p> <x:p> <x:em>Anders Moeller</x:em> is the author of another good book on the same topic. </x:p> </note>While any defines any element, the anyAttribute element specifies that an element of the defined type may have any number of any attribute. It has optional attributes namespace and processContents as in the case of the any elements. For instance, the following complex type defines a sequence of name and surname elements, a required attribute code as well as an attribute list from the XLink namespace. XLink attributes need not be declared and in any case are not validated:
<xs:element name="name" type="xs:string"/> <xs:element name="surname" type="xs:string"/> <xs:attribute name="code" type="xs:string"/> <xs:element name="author" type="authorType"/> <xs:complexType name="authorType"> <xs:sequence> <xs:element ref="name"/> <xs:element ref="surname"/> </xs:sequence> <xs:attribute ref="code" use="required"/> <xs:anyAttribute namespace="http://www.w3.org/1999/xlink" processContents="skip"/> </xs:complexType>
<xs:element name="author" type="authorType"/> <xs:group name="authorElements"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="surname" type="xs:string"/> </xs:sequence> </xs:group> <xs:attributeGroup name="authorAttributes"> <xs:attribute name="born" type="xs:date" use="required"/> <xs:attribute name="dead" type="xs:date"/> </xs:attributeGroup> <xs:complexType name="authorType"> <xs:group ref="authorElements"/> <xs:attributeGroup ref="authorAttributes"/> </xs:complexType>
The type constructor elements sequence, choice, all, any, and group have optional occurrence constraint attributes minOccurs and maxOccurs that indicate the minimum and maximum number of times they must repeat. The default is 1 for both and maxOccurs can be set to unbounded. In case of group, occurrence constraints must be specified on the group reference rather than on the group definition.
Moreover, the above described constructors can be combined (with the restrictions we said) as soon as the resulting content model is deterministic (as for DTD). The rationale behind this limitation (and many other) is that XML schema processors should be easier to implement.
Mixed content, that is a content mixing element markup and character data, can be obtained by setting to true the attribute mixed of the complexType element. If mixed is false (the default), only whitespace is permitted outside elements. It is not possible to contrain the character data in mixed content. An example follows:
<xs:element name="name" type="xs:string"/> <xs:element name="surname" type="xs:string"/> <xs:element name="author" type="authorType"/> <xs:complexType name="authorType" mixed="true"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element ref="name"/> <xs:element ref="surname"/> </xs:choice> </xs:complexType>
A valid instance of an element of type authorType follows:
<author> <name>Scott</name> <surname>Means</surname> is the author of an excellent book on XML. <name>Anders</name> <surname>Moeller</surname> is the author of another good book on the same topic. </author>
We have seen that simple types can be derived from other simple types by restriction. Also complex types can be derived from other complex types, typically by extention. This mechanism of complex type generation resembles type inheritance of object-oriented programming languages. We have seen above how to create a complex type with simple content by extending a primitive type using the simpleContent element:
<xs:complexType name="bookType"> <xs:simpleContent> <xs:extension base="xs:string"> <xs:attribute name="price" type="xs:decimal"/> </xs:extension> </xs:simpleContent> </xs:complexType>
To derive a complex type with complex content from another complex type we use the complexContent element. The following example defines a bibliography as an unbounded sequence of article or book items. Both the article and book types are extentions of a core item type that defines common attributes, like authors, title and year of publication. In particular, the article type adds a journal element and the book type adds a publisher element and an isbn attribute:
<xs:element name="author" type="xs:string"/> <xs:element name="title" type="xs:string"/> <xs:element name="year" type="xs:gYear"/> <xs:element name="journal" type="xs:string"/> <xs:element name="publisher" type="xs:string"/> <xs:element name="coreItem" type="coreItemType"/> <xs:element name="article" type="articleType"/> <xs:element name="book" type="bookType"/> <xs:element name="bibliography" type="bibliographyType"/> <xs:attribute name="isbn" type="xs:string"/> <xs:complexType name="coreItemType"> <xs:sequence> <xs:element ref="author" maxOccurs="unbounded"/> <xs:element ref="title"/> <xs:element ref="year"/> </xs:sequence> </xs:complexType> <xs:complexType name="articleType"> <xs:complexContent> <xs:extension base="coreItemType"> <xs:sequence> <xs:element ref="journal"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="bookType"> <xs:complexContent> <xs:extension base="coreItemType"> <xs:sequence> <xs:element ref="publisher"/> </xs:sequence> <xs:attribute ref="isbn"></xs:attribute> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="bibliographyType"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element ref="article"/> <xs:element ref="book"/> </xs:choice> </xs:complexType>
An instance of an extended type must have all the child elements of the base type followed by all the child elements declared in the extension. As an example, a valid bibliography instance follows:
<bibliography> <article> <author>Georg Gottlob</author> <title>XPath processing in a nutshell</title> <year>2003</year> <journal>SIGMOD Records</journal> </article> <book isbn="0-596-00764-7"> <author>Elliotte Harold</author> <author>Scott Means</author> <title>XML in a nutshell</title> <year>2004</year> <publisher>O'Reilly</publisher> </book> </bibliography>