Mixed Content

XML 1.0 provided the ability to declare an element that could contain parsed character data (#PCDATA) and unlimited occurrences of elements drawn from a provided list. Schemas provide the same functionality plus the ability to control the number and sequence in which elements appear within character data.

The mixed attribute of the complexType element controls whether character data may appear within the body of the element with which it is associated. To illustrate this concept, Example 17-9 gives us a new schema that will be used to validate form-letter documents.

This schema seems to declare a single element called letter that may contain character data and nothing else. But attempting to validate the following document produces an error, as shown in Example 17-10.

The following error is generated:

The content of element type "letter" must match "EMPTY".

This is because there's no complex content for the letter element. Setting mixed to true is not the same as declaring an element that may contain a string. The character data may only appear in relation to other complex content, which leads to the subject of relative element positioning.

You have already seen the xs:sequence element, which dictates that the elements it contains must appear in exactly the same order in which they appear within the sequence element. In addition to xs:sequence, schemas also provide the xs:choice and xs:all elements to control the order in which elements may appear. These elements may be nested to create sophisticated element structures.

Expanding the form-letter example, a sequence adds support for various letter components to the formletter.xsd schema:

<xs:element name="letter">
  <xs:complexType mixed="true">
    <xs:sequence>
      <xs:element name="greeting"/>
      <xs:element name="body"/>
      <xs:element name="closing"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

Now, thanks to the xs:sequence element, a letter must include a greeting element, a body element, and a closing element, in that order. But, in some cases, what is desired is that one and only one element appear from a collection of possibilities. The xs:choice element supports this. For example, if the greeting element needed to be restricted to contain only one salutation out of a permissible list, it could be declared to do so using xs:choice:

<xs:element name="greeting">
  <xs:complexType mixed="true">
    <xs:choice>
      <xs:element name="hello"/>
      <xs:element name="hi"/>
      <xs:element name="dear"/>
    </xs:choice>
  </xs:complexType>
</xs:element>

Now one of the permitted salutations must appear in the greeting element for the letter to be considered valid.

The remaining element-order enforcement construct is the xs:all element. Unlike the xs:sequence and xs:choice elements, the xs:all element must appear at the top of the content model and can only contain elements that are optional or appear only once. The xs:all construct tells the schema processor that each of the contained elements must appear once in the target document, but can appear in any order. This could be applied in the form letter example. If the form letter had certain elements that had to appear in the body element, but not in any particular order, xs:all could be used to control their appearance:

<xs:element name="body">
  <xs:complexType mixed="true">
    <xs:all>
      <xs:element name="item"/>
      <xs:element name="price"/>
      <xs:element name="arrivalDate"/>
    </xs:all>
  </xs:complexType>
</xs:element>

This would allow the letter author to mix these elements into the narrative without being restricted to any particular order. Also, it would prevent the author from inserting multiple references to the same value by accident. A valid document instance, including the new body content, might look like Example 17-11.

Just as the xs:attributeGroup element allows commonly used attributes to be grouped together and referenced as a unit, the xs:group element allows sequences, choices, and model groups of individual element declarations to be grouped together and given a unique name. These groups can then be included in another element-content model using an xs:group element with the ref attribute set to the same value as the name attribute of the source group. When you do this, any occurrence constraints have to be specified on the reference to the group rather than on the definition of the group.