Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Deriving Types

Simple types are derived by restriction, as Simple Types and Facets showed. Complex types can be derived too, in two ways: extension adds content to a type, and restriction narrows it. A derived type can be used wherever its base type is expected, which gives XSD something like the subclasses of object-oriented languages.

Extension

vane.xsd already has two extensions, of a simple type. A title is an xs:string with an abbrev attribute, and a sourcecode is an xs:string with a type and a name:

  <xs:complexType name="title">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="abbrev" type="xs:string"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

xs:simpleContent with xs:extension adds attributes to a simple type, and the result is a complex type with simple content.

An editor who works on the draft keeps a worksheet: the paragraphs to look at, some source code, and the day the next draft is due. Its schema, xsd-derivation.xsd, includes vane.xsd and derives new types from the draft's:

xsd-derivation.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<!-- An editor's worksheet for the draft: blocks of the draft to work on,
     and the day the next draft is due. It derives types from vane.xsd's
     and makes the blocks a substitution group. -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:include schemaLocation="vane.xsd"/>

  <!-- Restriction: a requirement is a paragraph whose anchor is required. -->
  <xs:complexType name="requirement" mixed="true">
    <xs:complexContent>
      <xs:restriction base="text">
        <xs:group ref="inline" minOccurs="0" maxOccurs="unbounded"/>
        <xs:attribute name="anchor" type="anchor" use="required"/>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>

  <!-- Extension: a changed requirement also says since which draft. -->
  <xs:complexType name="changed-requirement" mixed="true" final="#all">
    <xs:complexContent>
      <xs:extension base="requirement">
        <xs:attribute name="since" type="draft-name" use="required"/>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

  <!-- A substitution group: t, dl and sourcecode can stand in for block. -->
  <xs:element name="block" abstract="true"/>
  <xs:element name="t" type="text" substitutionGroup="block"/>
  <xs:element name="dl" type="definition-list" substitutionGroup="block"/>
  <xs:element name="sourcecode" type="sourcecode" substitutionGroup="block"/>

  <xs:element name="worksheet">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="block" maxOccurs="unbounded"/>
        <xs:element name="due" type="xs:date" nillable="true"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

changed-requirement extends requirement with a since attribute that names the draft that changed it. An extension's content is the base type's content followed by its own, and its attributes are the base type's and its own. Here only an attribute is added, so a changed requirement has the mixed content of every paragraph.

Restriction

A restriction of a complex type allows a subset of what its base type allows. It writes out the whole content again, narrowed. requirement is a text whose anchor is required: every requirement is a valid paragraph, but not every paragraph is a requirement. sxml checks, when it compiles the schema, that a restriction really narrows. Making the anchor optional again in a restriction of requirement is an error:

$ echo '<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:include schemaLocation="xsd-derivation.xsd"/>
  <xs:complexType name="loose-requirement" mixed="true">
    <xs:complexContent>
      <xs:restriction base="requirement">
        <xs:group ref="inline" minOccurs="0" maxOccurs="unbounded"/>
        <xs:attribute name="anchor" type="anchor"/>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:schema>' | sxml validate --schema=- xsd-worksheet.xml
-:3:3: derivation-ok-restriction.3: The attributes of a restriction are not allowed by its base

Choosing the type in the document: xsi:type

The worksheet declares its paragraphs as t with the type text. In the document, the attribute xsi:type says that a paragraph has a type derived from text. xsi is the namespace http://www.w3.org/2001/XMLSchema-instance, which XSD reserves for attributes that talk to the validator:

xsd-worksheet.xml:

<worksheet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <t xsi:type="requirement" anchor="req-3">A rolling-out flag
  <bcp14>SHOULD</bcp14> return to the state "stable" ten minutes after
  its rollout step began.</t>
  <t xsi:type="changed-requirement" anchor="req-7"
     since="draft-example-vane-protocol-01">After three failed checks in
  a row, a service <bcp14>MUST</bcp14> put the flag in a pause.</t>
  <t>Check the pause section against the state machine.</t>
  <sourcecode type="abnf">percentage = 1*3DIGIT</sourcecode>
  <due xsi:nil="true"/>
</worksheet>
$ sxml validate --schema=xsd-derivation.xsd xsd-worksheet.xml

Each t is validated by the type its xsi:type names, or by text when it has none. These mistakes show the rules:

bad-worksheet.xml:

<worksheet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <t xsi:type="requirement">A flag <bcp14>MUST</bcp14> start stable.</t>
  <t xsi:type="changed-requirement" anchor="req-8">A pause
  <bcp14>SHOULD</bcp14> last five minutes.</t>
  <t xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">Plain.</t>
  <block/>
  <figure>A drawing of the states.</figure>
  <due/>
</worksheet>
$ sxml validate --schema=xsd-derivation.xsd bad-worksheet.xml
bad-worksheet.xml:2:3: cvc-complex-type.4: element 't' lacks the required attribute 'anchor'
bad-worksheet.xml:3:3: cvc-complex-type.4: element 't' lacks the required attribute 'since'
bad-worksheet.xml:5:6: cvc-elt.4.3: xsi:type gives element 't' type 'xs:string', which is not validly derived from its declared type, type 'text'
bad-worksheet.xml:6:3: cvc-elt.2: element 'block' has an abstract declaration
bad-worksheet.xml:7:3: cvc-complex-type.2.4: element 'figure' is not allowed here; expected 'block' or 'due'
bad-worksheet.xml:8:3: cvc-datatype-valid.1.2.1: element 'due' has the value '', which is not valid for type 'xs:date'
  • The first paragraph says it is a requirement, but has no anchor.
  • The second is a changed requirement without its since.
  • The third names a type that isn't derived from text at all. sxml ignores that xsi:type, and validates the paragraph as text.

The last three are about the substitution group and xsi:nil, below.

Types derived from the declared type are what make queries over mixed content easy. With the schema's types, element(*, requirement) matches every element whose type is requirement or derived from it, so the changed requirement counts too:

$ sxml xquery --method=adaptive --context-item=xsd-worksheet.xml --expression='
    import schema default element namespace "" at "xsd-derivation.xsd";
    (validate { / })//t[. instance of element(*, requirement)]/@anchor/string()'
"req-3"
"req-7"

A type can be abstract too. abstract="true" on a complex type means that no element may have that type itself, only a type derived from it; it is a type that exists to be derived from. Made abstract, requirement can no longer be named in xsi:type, while changed-requirement still can:

$ sed 's/<xs:complexType name="requirement" mixed="true">/<xs:complexType name="requirement" mixed="true" abstract="true">/' \
    xsd-derivation.xsd > abstract.xsd
$ sxml validate --schema=abstract.xsd xsd-worksheet.xml
xsd-worksheet.xml:2:3: cvc-type.2: the type of element 't' is abstract: type 'requirement'

Substitution groups

xsi:type keeps one element name and varies the type. A substitution group varies the element name instead: a global element declares that it can stand in for another, the head of the group, wherever the head is allowed. In the worksheet, t, dl and sourcecode are members of the group whose head is block, and the worksheet's content model names only block.

block is abstract="true", so it can't appear itself; only its members can. That was the fourth mistake above. The fifth, a figure, is no member of the group, and the message names the head, block, which stands for every member. A member's type must be the head's type or derived from it; block has no type, which means xs:anyType, so any type will do.

In XQuery and XSLT, schema-element(block) matches the head and all its members:

$ sxml xquery --method=adaptive --context-item=xsd-worksheet.xml --expression='
    import schema default element namespace "" at "xsd-derivation.xsd";
    (validate { / })/worksheet/*[. instance of schema-element(block)] ! name()'
"t"
"t"
"t"
"sourcecode"

In vane.xsd, a section's blocks are a choice of three local elements. A substitution group is the open alternative: another schema that includes this one can add a member, a table say, and every content model that allows a block allows it too, without a change.

Nothing, explicitly

An element that is required but has no value can be declared nillable="true". The document then says xsi:nil="true", and the element must be empty: the worksheet's due is there, but no day is set yet. That's different from an empty element, which is an empty string, and an empty string is not a date: the sixth mistake.

In XPath, nilled tells a nilled element from others:

$ sxml xquery --method=adaptive --context-item=xsd-worksheet.xml --expression='
    import schema default element namespace "" at "xsd-derivation.xsd";
    (validate { / })//due ! nilled(.)'
true()

Controlling derivation

A schema can forbid what others may do with its types. final on a type forbids deriving from it (final="extension", "restriction" or "#all"), and block on a type or element forbids using derived types in documents, through xsi:type or substitution. changed-requirement is final, so a schema that extends it is in error:

$ echo '<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:include schemaLocation="xsd-derivation.xsd"/>
  <xs:complexType name="reviewed-requirement" mixed="true">
    <xs:complexContent>
      <xs:extension base="changed-requirement">
        <xs:attribute name="reviewer" type="xs:string"/>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
</xs:schema>' | sxml validate --schema=- xsd-worksheet.xml
-:3:3: cos-ct-extends.1.1: The base type's final forbids extension

Use them for types whose meaning would break if someone extended them, or for elements that must have exactly their declared type.