facebook google plus twitter
Webucator's Free XML Schema Tutorial

Lesson: Complex-Type Elements

Welcome to our free XML Schema tutorial. This tutorial is based on Webucator's Introduction to XML Schema course.

Tutorial

Lesson Goals

  • To declare complex-type elements.
  • To use content models.
  • To control cardinality (quantity of elements).
  • To declare elements of mixed types.
  • To define named complex types.

Overview

Complex-type elements have attributes, child elements, or some combination of the two. For example, the Name and HomePage elements below are both complex-type elements.

Code Sample:

ComplexTypes/Demos/ComplexType.xml
<?xml version="1.0"?>
<Person>
	<Name>
		<FirstName>Mark</FirstName>
		<LastName>Twain</LastName>
	</Name>
	<HomePage URL="http://www.marktwain.com"/>
</Person>

As the diagram below shows, a complex-type element can be empty, contain simple content such as a string, or can contain complex content such as a sequence of elements.

Whereas it is not necessary to explicitly declare that a simple-type element is a simple type, it is necessary to specify that a complex-type element is a complex type. This is done with the xs:complexType element as shown below.

<xs:element name="ElementName"> <xs:complexType> <!--Content Model Goes Here--> </xs:complexType> </xs:element>

Content Models

Content models are used to indicate the structure and order in which child elements can appear within their parent element. Content models are made up of model groups. The three types of model groups are listed below.

  1. xs:sequence - the elements must appear in the order specified.
  2. xs:all - the elements must appear, but order is not important.
  3. xs:choice - only one of the elements can appear.

xs:sequence

The following sample shows the syntax for declaring a complex-type element as a sequence, meaning that the elements must show up in the order they are declared.

<xs:element name="ElementName"> <xs:complexType> <xs:sequence> <xs:element name="Child1" type="xs:string"/> <xs:element name="Child2" type="xs:string"/> <xs:element name="Child3" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element>

xs:all

The following sample shows the syntax for declaring a complex-type element as a conjunction, meaning that the elements can show up in any order.

<xs:element name="ElementName"> <xs:complexType> <xs:all> <xs:element name="Child1" type="xs:string"/> <xs:element name="Child2" type="xs:string"/> <xs:element name="Child3" type="xs:string"/> </xs:all> </xs:complexType> </xs:element>

xs:choice

The following sample shows the syntax for declaring a complex-type element as a choice, meaning that only one of the child elements may show up.

<xs:element name="ElementName"> <xs:complexType> <xs:choice> <xs:element name="Child1" type="xs:string"/> <xs:element name="Child2" type="xs:string"/> <xs:element name="Child3" type="xs:string"/> </xs:choice> </xs:complexType> </xs:element>

Complex Model Groups

In the examples above, the model groups are all made up of simple-type elements. However, complex-type elements can contain other complex-type elements.

<xs:element name="ElementName"> <xs:complexType> <xs:choice> <xs:element name="Child1" type="xs:string"/> <xs:element name="Child2"> <xs:complexType> <xs:sequence> <xs:element name="GC1" type="xs:string"/> <xs:element name="GC2" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="Child3" type="xs:string"/> </xs:choice> </xs:complexType> </xs:element>

Furthermore, model groups can be nested within each other. The following example illustrates this. Notice that the choice model group, which allows for either a Salary element or a Wage element is nested with in a sequence model group. Both of the subsequent instances are valid according to this schema.

Code Sample:

ComplexTypes/Demos/Employee.xsd
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
	<xs:simpleType name="Salary">
		<xs:restriction base="xs:decimal">
			<xs:minInclusive value="10000"/>
			<xs:maxInclusive value="90000"/>
		</xs:restriction>
	</xs:simpleType>
	<xs:element name="Employee">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="Name">
					<xs:complexType>
						<xs:sequence>
							<xs:element name="FirstName"/>
							<xs:element name="LastName"/>
						</xs:sequence>
					</xs:complexType>
				</xs:element>
				<xs:choice>
					<xs:element name="Salary" type="Salary"/>
					<xs:element name="Wage" type="xs:decimal"/>
				</xs:choice>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
</xs:schema>

Code Sample:

ComplexTypes/Demos/DaveSmith.xml
<?xml version="1.0"?>
<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:noNamespaceSchemaLocation="Employee.xsd">
	<Name>
		<FirstName>Dave</FirstName>
		<LastName>Smith</LastName>
	</Name>
	<Salary>90000</Salary>
</Employee>

Code Sample:

ComplexTypes/Demos/JillSmith.xml
<?xml version="1.0"?>
<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:noNamespaceSchemaLocation="Employee.xsd">
	<Name>
		<FirstName>Jill</FirstName>
		<LastName>Smith</LastName>
	</Name>
	<Wage>20.50</Wage>
</Employee>

Occurrence Constraints

By default, elements that are declared locally must show up once and only once within their parent. This constraint can be changed using the minOccurs and maxOccurs attributes. The default value of each of these attributes is 1. The value of minOccurs can be any non-negative integer. The value of maxOccurs can be any positive integer or unbounded, meaning that the element can appear an infinite number of times.

The example below shows how minOccurs can be used to make an element optional and how maxOccurs can be used to allow an element to be repeated indefinitely.

Code Sample:

ComplexTypes/Demos/Employee2.xsd
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
	<xs:simpleType name="Salary">
		<xs:restriction base="xs:decimal">
			<xs:minInclusive value="10000"/>
			<xs:maxInclusive value="90000"/>
		</xs:restriction>
	</xs:simpleType>
	<xs:element name="Employee">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="Name">
					<xs:complexType>
						<xs:sequence>
							<xs:element name="FirstName"/>
							<xs:element name="MiddleName" minOccurs="0"/>
							<xs:element name="LastName"/>
						</xs:sequence>
					</xs:complexType>
				</xs:element>
				<xs:choice>
					<xs:element name="Salary" type="Salary"/>
					<xs:element name="Wage" type="xs:decimal"/>
				</xs:choice>
				<xs:element name="Responsibilities">
					<xs:complexType>
						<xs:sequence>
							<xs:element name="Responsibility" type="xs:string" 
								maxOccurs="unbounded"/>
						</xs:sequence>
					</xs:complexType>
				</xs:element>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
</xs:schema>

Note that minOccurs and maxOccurs can also be applied to model groups (e.g, xs:sequence) to control the number of times a model group can be repeated.

Adding ComplexType Elements

Duration: 15 to 25 minutes.

In this exercise, you will modify the song schema, adding model groups and occurrence constraints.

  1. Open ComplexTypes/Exercises/Song.xsd for editing.
  2. Modify the schema so that the Song element's children can appear in any order.
  3. Change the content model of the Song element so that it contains an Artists element in place of the Artist element.
  4. Make the Artists element allow for one or more child Artist elements. Note that the Artist element is already declared globally.
  5. Make the Length element optional.
  6. Try to validate ComplexTypes/Exercises/WeAreTheWorld.xml against the schema you just created. If the XML document is invalid, fix your schema.

Solution:

ComplexTypes/Solutions/Song.xsd
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
---- C O D E   O M I T T E D ----

	<xs:element name="Song">
		<xs:complexType>
			<xs:all>
				<xs:element ref="Title"/>
				<xs:element ref="Year"/>
				<xs:element name="Artists">
					<xs:complexType>
						<xs:sequence>
							<xs:element ref="Artist" maxOccurs="unbounded"/>
						</xs:sequence>
					</xs:complexType>
				</xs:element>
				<xs:element ref="Length" minOccurs="0"/>
			</xs:all>
		</xs:complexType>
	</xs:element>
</xs:schema>

Declaring Global Complex-Type Elements

As with simple-type elements, complex-type elements can be declared globally by placing the element declaration as a child of the xs:schema element.

Globally declared elements cannot take occurrence constraints. However, the minOccurs and maxOccurs constraints can be applied to references to globally declared elements. To illustrate, look at the following example. Notice that all elements, both simple-type and complex-type, are declared globally and then referenced within the model groups. Some of the references (e.g, Responsibilities) have occurrence constraints assigned to them.

Code Sample:

ComplexTypes/Demos/Employee3.xsd
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
	<xs:simpleType name="Salary">
		<xs:restriction base="xs:decimal">
			<xs:minInclusive value="10000"/>
			<xs:maxInclusive value="90000"/>
		</xs:restriction>
	</xs:simpleType>
	<xs:element name="Name">
		<xs:complexType>
			<xs:sequence>
				<xs:element ref="FirstName"/>
				<xs:element ref="MiddleName" minOccurs="0"/>
				<xs:element ref="LastName"/>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
	<xs:element name="FirstName"/>
	<xs:element name="MiddleName"/>
	<xs:element name="LastName"/>
	<xs:element name="Wage" type="xs:decimal"/>
	<xs:element name="Salary" type="Salary"/>
	<xs:element name="Responsibilities">
		<xs:complexType>
			<xs:sequence>
				<xs:element ref="Responsibility" maxOccurs="unbounded"/>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
	<xs:element name="Responsibility" type="xs:string"/>
	<xs:element name="Employee">
		<xs:complexType>
			<xs:sequence>
				<xs:element ref="Name"/>
				<xs:choice>
					<xs:element ref="Salary"/>
					<xs:element ref="Wage"/>
				</xs:choice>
				<xs:element ref="Responsibilities" minOccurs="0"/>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
</xs:schema>

Converting Complex-Type Elements from Local to Global

Duration: 10 to 15 minutes.

In this exercise, you will convert the Artists element declaration in the song schema from local to global.

  1. Open ComplexTypes/Exercises/Song.xsd and save it as Song2.xsd in the same directory.
  2. Change the Artists element to be declared globally.
  3. Try to validate ComplexTypes/Exercises/TheGirlIsMine.xml against the schema you just created. If the XML document is invalid, fix your schema.

Solution:

ComplexTypes/Solutions/Song2.xsd
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
---- C O D E   O M I T T E D ----

	<xs:element name="Song">
		<xs:complexType>
			<xs:all>
				<xs:element ref="Title"/>
				<xs:element ref="Year"/>
				<xs:element ref="Artists"/>
				<xs:element ref="Length" minOccurs="0"/>
			</xs:all>
		</xs:complexType>
	</xs:element>
	<xs:element name="Artists">
		<xs:complexType>
			<xs:sequence>
				<xs:element ref="Artist" maxOccurs="unbounded"/>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
</xs:schema>

Mixed Content

Sometimes an element will contain both child elements and character text. For example, a para element might contain mostly plain character text, but it could also have other elements (e.g, emphasis) littered throughout the character text.

As an example, let's examine look at the following XML instance document.

Code Sample:

ComplexTypes/Demos/PaulMcCartney.xml
<?xml version="1.0"?>
<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:noNamespaceSchemaLocation="Employee4.xsd">
	<Name>
		<FirstName>Paul</FirstName>
		<LastName>McCartney</LastName>
	</Name>
	<Salary>90000</Salary>
	<Bio>
		Worked for <Company>the Beatles</Company> as a 
			<JobTitle>Singer</JobTitle>.
		Worked for <Company>the Beatles</Company> as a 
			<JobTitle>Bass Guitarist</JobTitle>.
		Worked for <Company>the Wings</Company> as a 
			<JobTitle>Singer</JobTitle>.
	</Bio>
</Employee>

Notice that the Bio element contains child elements Company and JobTitle as well as character text. Such elements are said to contain mixed content. The syntax for declaring elements with mixed content is shown below.

<xs:element name="ElementName"> <xs:complexType mixed="true"> <xs:sequence> <xs:element name="Child1" type="xs:string"/> <xs:element name="Child2" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element>

The following example illustrates how to define this in our employee schema.

Code Sample:

ComplexTypes/Demos/Employee4.xsd
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
---- C O D E   O M I T T E D ----

				<xs:element name="Bio">
					<xs:complexType mixed="true">
						<xs:sequence maxOccurs="unbounded">
							<xs:element name="Company" type="xs:string"/>
							<xs:element name="JobTitle" type="xs:string"/>
						</xs:sequence>
					</xs:complexType>
				</xs:element>
---- C O D E   O M I T T E D ----

</xs:schema>

Defining Complex Types Globally

As with simple types, complex types can be defined globally. The example below shows how this is done.

Code Sample:

ComplexTypes/Demos/Author.xsd
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
	<xs:complexType name="Person">
		<xs:sequence>
			<xs:element name="FirstName" type="xs:string"/>
			<xs:element name="LastName" type="xs:string"/>
		</xs:sequence>
	</xs:complexType>
	<xs:element name="Author" type="Person"/>
</xs:schema>

As you can see, complex types are defined with the xs:complexType element. The major advantage of defining a complex type globally is that it can be reused. For example, a schema might allow for an Illustrator element as well as an Author element. Both elements could be of type Person. This way, if the Person type is changed later, the change will apply to both elements.

The instance document below will validate properly against the schema above.

Code Sample:

ComplexTypes/Demos/MarkTwain.xml
<?xml version="1.0"?>
<Author xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:noNamespaceSchemaLocation="Author.xsd">
	<FirstName>Mark</FirstName>
	<LastName>Twain</LastName>
</Author>