Chapitre 5. Schémas XML

5.1. Introduction

Les schémas XML permettent, comme les DTD, de définir des modèles de documents. Il est ensuite possible de vérifier qu'un document donné est valide pour un schéma, c'est-à-dire respecte les contraintes données par le schéma. Les schémas ont été introduits pour combler certaines lacunes des DTD.

5.1.1. Comparaison avec les DTD

La première différence entre les schémas et les DTD est d'ordre syntaxique. La syntaxe des DTD est une syntaxe héritée de SGML qui est différente de la syntaxe XML pour le corps des documents. En revanche, la syntaxe des schémas est une syntaxe purement XML. Un schéma est, en effet, un document XML à part entière avec un élément racine xsd:schema et un espace de noms.

Les DTD manquent cruellement de précision dans la description des contenus des éléments. Cette lacune se manifeste surtout au niveau des contenus textuels et des contenus mixtes. Il est, par exemple, impossible d'imposer des contraintes sur les contenus textuels des éléments. Le seul type possible pour les contenus textuels est #PCDATA qui autorise toutes les chaînes de caractères. Les types pour les attributs sont un peu plus nombreux mais ils restent encore très limités. À l'inverse, les schémas possèdent une multitude de types prédéfinis pour les contenus textuels. Ces types couvrent les chaînes de caractères, les nombres comme les entiers et les flottants ainsi que les heures et les dates. Ces types peuvent, en outre, être affinés par des mécanismes de restriction et d'union. Il est possible de définir, à titre d'exemple, des types pour les entiers entre 1 et 12, les flottants avec deux décimales ou les chaînes d'au plus 16 caractères ne comportant que des chiffres et des tirets '-'.

Les DTD sont encore plus limitées dans la description des contenus mixtes. La seule possibilité est d'exprimer que le contenu d'un élément est un mélange, sans aucune contrainte, de texte et de certains éléments. Les schémas comblent cette lacune en permettant d'avoir des contenus mixtes aussi précis que les contenus purs qui décrivent l'ordre et les nombres d'occurrences des enfants d'un élément.

Dans une DTD, le contenu pur d'un élément est décrit directement par une expression rationnelle. Les schémas procèdent en deux étapes. Ils définissent des types qui sont ensuite associés aux éléments. Les schémas se distinguent des DTD par leurs possibilités de définir de nouveaux types. Il est d'abord possible de construire des types explicitement à la manière des DTD. Il existe ensuite des mécanismes permettant de définir un nouveau type à partir d'un autre type, soit prédéfini, soit déjà défini dans le schéma. Ce nouveau type est obtenu soit par extension soit par restriction du type de départ. L'extension consiste à enrichir le type en ajoutant du contenu et des attributs. La restriction consiste, à l'inverse, à ajouter des contraintes pour restreindre les contenus valides. Ces deux mécanismes permettent ainsi de construire une véritable hiérarchie de types semblable à l'approche orientée objet des langages comme Java ou C++.

Les schémas autorisent des facilités impossibles avec les DTD. La première est la possibilité d'avoir plusieurs éléments locaux avec des noms identiques mais avec des types et donc des contenus différents. Dans une DTD, un élément a une seule déclaration qui décrit ses contenus possibles pour toutes ses occurrences dans un document. Une deuxième facilité est formée des mécanismes de substitution de types et d'éléments. À titre d'exemple, un schéma peut prévoir qu'un élément puisse se substituer à un autre élément. Ces substitutions fonctionnent de pair avec la hiérarchie des types.

Les DTD ont une modularité très limitée et l'écriture de DTD d'envergure est un exercice difficile. Les seuls dispositifs mis à disposition des auteurs de DTD sont l'import de DTD externes et les entités paramètres. Les schémas possèdent plusieurs mécanismes destinés à une plus grande modularité. Le premier d'entre eux est la possibilité, pour les schémas, de définir des types par extension et restriction. Il existe également les groupes d'éléments et les groupes d'attributs.

Les DTD proviennent de SGML et sont antérieures aux espaces de noms. Pour cette raison, elles ne les prennent pas en compte. La déclaration d'un élément se fait en donnant le nom qualifié de l'élément avec le préfixe et le nom local. Ceci impose, dans les documents, d'associer l'espace de noms à ce même préfixe. Ceci est contraire à l'esprit des espaces de noms où le préfixe est juste une abréviation interchangeable pour l'URI de l'espace de noms. Les schémas, au contraire, prennent en compte les espaces de noms. Un schéma déclare d'abord un espace de noms cible. Les éléments et les attributs sont ensuite déclarés, dans le schéma, avec leur nom local. Un document qui mélange des éléments et des attributs provenant d'espaces de noms différents peut encore être validé à l'aide des différents schémas pour les espaces de noms.

5.2. Un premier exemple

Voici un exemple de schéma XML définissant le type de document de la bibliographie. Ce schéma est volontairement rudimentaire pour un premier exemple. Il n'est pas très précis sur les contenus de certains éléments. Un exemple plus complet pourrait être donné pour la bibliographie.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">1
  <xsd:annotation>2
    <xsd:documentation xml:lang="fr">
      Schéma XML pour bibliography.xml
    </xsd:documentation>
  </xsd:annotation>
  <xsd:element name="bibliography" type="Bibliography"/>3

  <xsd:complexType name="Bibliography">4
    <xsd:sequence>
      <xsd:element name="book" minOccurs="1" maxOccurs="unbounded">5
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="title"     type="xsd:string"/>
            <xsd:element name="author"    type="xsd:string"/>
            <xsd:element name="year"      type="xsd:string"/>
            <xsd:element name="publisher" type="xsd:string"/>
            <xsd:element name="isbn"      type="xsd:string"/>
            <xsd:element name="url"       type="xsd:string" minOccurs="0"/>
          </xsd:sequence>
          <xsd:attribute name="key"  type="xsd:NMTOKEN" use="required"/>6
          <xsd:attribute name="lang" type="xsd:NMTOKEN" use="required"/>7
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

1

Élément racine xsd:schema avec la déclaration de l'espace de noms des schémas associé au préfixe xsd.

2

Documentation du schéma.

3

Déclaration de l'élément bibliography avec le type Bibliography.

4

Début de la définition du type Bibliography.

5

Déclaration de l'élément book dans le contenu du type Bibliography.

6 7

Déclaration des attributs key et lang de l'élément book avec le type xsd:NMTOKEN.

Ce schéma déclare l'élément bibliography du type Bibliography qui est ensuite introduit par l'élément xsd:complexType. Ce type est alors défini comme une suite d'autres éléments introduite par le constructeur xsd:sequence. Les deux attributs key et lang de l'élément book sont introduits par les déclarations xsd:attribute.

Dans tout ce chapitre, la convention suivante est appliquée. Les noms des éléments sont en minuscules alors que les noms des types commencent par une majuscule comme les classes du langage Java. Les noms de l'élément et de son type ne se différencient souvent que par la première lettre comme bibliography et Bibliography dans l'exemple précédent.

Comme pour les DTD, il existe des sites WEB permettant de valider un document vis à vis d'un schéma. Cette validation peut également être effectuée avec le logiciel xmllint. L'option --schema permet de passer en paramètre un schéma en plus du document XML.

5.3. Structure globale d'un schéma

Un schéma XML se compose essentiellement de déclarations d'éléments et d'attributs et de définitions de types. Chaque élément est déclaré avec un type qui peut être, soit un des types prédéfinis, soit un nouveau type défini dans le schéma. Le type spécifie quels sont les contenus valides de l'élément ainsi que ses attributs. Un nouveau type est obtenu soit par construction, c'est-à-dire une description explicite des contenus qu'il autorise, soit par dérivation, c'est-à-dire modification d'un autre type. Un schéma peut aussi contenir des imports d'autres schémas, des définitions de groupes d'éléments et d'attributs et des contraintes de cohérences.

L'espace de noms des schémas XML est identifié par l'URI http://www.w3.org/2001/XMLSchema. Il est généralement associé, comme dans l'exemple précédent au préfixe xsd ou à xs. Tout le schéma est inclus dans l'élément xsd:schema. La structure globale d'un schéma est donc la suivante.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Déclarations d'éléments, d'attributs et définitions de types -->
  ...
</xsd:schema>

Les éléments, attributs et les types peuvent être globaux ou locaux. Ils sont globaux lorsque leur déclaration ou leur définition est un enfant direct de l'élément racine xsd:schema. Sinon, ils sont locaux. Seuls les objets globaux peuvent être référencés dans tout le schéma pour être utilisés. Dans le premier exemple de schéma donné ci-dessus, l'élément bibliography est global alors que l'élément title est local. La déclaration de ce dernier intervient au sein de la définition du type Bibliography qui est global. Seuls les types globaux d'un schéma sont nommés. Les types locaux sont anonymes et n'ont pas de nom. Dans le schéma ci-dessus, le type de l'élément book est, par exemple, anonyme. Les éléments et attributs sont toujours nommés, qu'ils soient globaux ou locaux. Ils doivent, bien sûr, avoir un nom pour apparaître dans un document.

Les objets globaux et locaux se comportent différement vis à vis de l'espace de noms cible du schéma. Les objets globaux appartiennent toujours à cet espace de noms. Les objets locaux, au contraire, appartiennent ou n'appartiennent pas à l'espace de noms cible suivant les valeurs des attributs form, elementFormDefault et attributeFormDefault.

Les éléments sont déclarés par l'élément xsd:element et les attributs par l'élément xsd:attribute. Les types sont définis par les éléments xsd:simpleType et xsd:complexType.

5.3.1. Attributs de l'élément xsd:schema

L'élément racine xsd:schema peut avoir les attributs suivants. Ceux-ci permettent de spécifier l'espace de noms cible du schéma et de préciser quelques comportements par défaut du schéma.

targetNamespace

La valeur de cet attribut est l'URI qui identifie l'espace de noms cible, c'est-a-dire l'espace de noms des éléments et types définis par le schéma. Si cet attribut est absent, les élements et types définis n'ont pas d'espace de noms.

elementFormDefault et attributeFormDefault

Ces deux attributs donnent la valeur par défaut de l'attribut form pour respectivement les éléments et les attributs. Les valeurs possibles sont qualified et unqualified. La valeur par défaut est unqualified.

blockDefault et finalDefault

Ces deux attributs donnent la valeur par défaut des attributs block et final. Les valeurs possibles pour blockDefault sont #all ou une liste de valeurs parmi les valeurs extension, restriction et substitution. Les valeurs possibles pour finalDefault sont #all ou une liste de valeurs parmi les valeurs extension, restriction, list et union.

Dans l'exemple suivant, le schéma déclare que l'espace de noms cible est identifié par l'URI http://www.omega-one.org/~carton et que tous les éléments doivent être qualifiés dans les documents valides. L'espace de noms par défaut est déclaré égal à l'espace de noms cible afin de simplifier l'écriture du schéma.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema targetNamespace="http://www.omega-one.org/~carton"
            elementFormDefault="qualified"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.omega-one.org/~carton">
  ...

5.3.2. Référence explicite à un schéma

Il est possible dans un document de donner explicitement le schéma devant servir à le valider. On utilise un des attributs schemaLocation ou noNamespaceSchemaLocation dans l'élément racine du document à valider. Ces deux attributs se trouvent dans l'espace de noms des instances de schémas identifié par l'URI http://www.w3.org/2001/XMLSchema-instance. L'attribut schemaLocation est utilisé lors de l'utilisation d'espaces de noms alors que l'attribut noNamespaceSchemaLocation est utilisé lorsque le document n'utilise pas d'espace de noms

La valeur de l'attribut schemaLocation est une suite d'URI séparés par des espaces. Ces URI vont par paires et le nombre d'URI doit donc être pair. Le premier URI de chaque paire identifie un espace de noms et le second donne l'adresse du schéma à utiliser pour les éléments et attributs dans cet espace de noms. L'espace de noms identifié par le premier URI doit donc être l'espace de noms cible du schéma donné par le second. La valeur de l'attribut schemaLocation prend donc la forme générale suivante

schemaLocation="namespace1 schema1 namespace2 ... namespaceN schemaN"

namespacei est l'espace de noms cible du schéma schemai.

Le logiciel qui effectue la validation se base sur la valeur de l'attribut schemaLocation pour chercher la définition de chaque élément ou attribut dans le schéma correspondant à son espace de noms.

La valeur de l'attribut noNamespaceSchemaLocation est simplement l'URL d'un unique schéma qui doit permettre de valider l'intégralité du document. Il n'est, en effet, pas possible de distinguer les éléments qui n'ont pas d'espace de noms.

Dans l'exemple suivant, le document déclare que le schéma se trouve dans le fichier local bibliography.xsd et que l'espace de noms cible de ce schéma est identifié par l'URI http://www.omega-one.org/~carton/.

<?xml version="1.0" encoding="iso-8859-1"?>
<bibliography xsi:schemaLocation="http://www.omega-one.org/~carton/
                                  bibliography.xsd"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  ...

5.3.3. Documentation

L'élément xsd:annotation permet d'ajouter des commentaires dans un schéma. Il peut être enfant de l'élément xsd:schema pour des commentaires globaux. Il peut également être enfant des éléments xsd:element, xsd:attribute pour ajouter des commentaires aux déclarations d'éléments et d'atttributs ainsi que de xsd:simpleType et xsd:complexType pour ajouter des commentaires aux définitions de type. Contrairement aux commentaires XML, ces commentaires font partie à part entière du schéma XML et constituent sa documentation.

<xsd:annotation>
  <xsd:documentation xml:lang="fr">
    Commentaire en français
  </xsd:documentation>
  <xsd:appInfo>
    Information destinée aux applications
  </xsd:appInfo>
</xsd:annotation>

5.4. Déclarations d'éléments

Pour qu'un document soit valide pour un schéma, tout élément apparaissant dans le document doit être déclaré dans le schéma. Cette déclaration lui donne un type qui détermine, d'une part, les contenus possibles et, d'autre part, les attributs autorisés et obligatoires. Contrairement aux DTD, les attributs ne sont pas directement associés aux éléments. Ils font partie des types qui sont donnés aux éléments.

Le type donné à un élément peut être soit un type nommé soit un type anonyme. Dans le premier cas, le type est soit un type prédéfini dont le nom fait partie de l'espace de noms des schémas soit un type défini globalement dans le schéma. Dans le second cas, le type est défini explicitement à la déclaration de l'élément.

5.4.1. Type nommé

La déclaration la plus simple d'un élément prend la forme suivante.

<xsd:element name="element" type="type"/>

element et type sont respectivement le nom et le type de l'élément. Ce type peut être un des types prédéfinis comme xsd:string ou xsd:integer ou encore un type défini dans le schéma. L'exemple suivant déclare l'élément title de type xsd:string. Le nom du type doit être un nom qualifié comme ici par le préfixe xsd associé à l'espace de noms des schémas. Cette règle s'applique aussi lorsque le type est défini dans le schéma.

<xsd:element name="title" type="xsd:string"/>
<xsd:element name="title" type="tns:Title"/>

5.4.2. Valeur par défaut et valeur fixe

Lorsque le type est simple, il est possible de donner une valeur par défaut ou une valeur fixe à l'élément comme dans les deux exemples suivants. Il faut pour cela donner des valeurs aux attributs default ou fixed de l'élément xsd:element.

<xsd:element name="title" type="xsd:string" default="Titre par défaut"/>
<xsd:element name="title" type="xsd:string" fixed="Titre fixe"/>

5.4.3. Type anonyme

Lors de la déclaration d'un élément, il est possible de décrire explicitement le type. La déclaration du type est alors le contenu de l'élément xsd:element. Le type est alors local et sa déclaration prend alors une des deux formes suivantes où element est le nom de l'élément déclaré.

<xsd:element name="element"> 
  <xsd:simpleType>
  ...
  </xsd:simpleType>
</xsd:element>
<xsd:element name="element"> 
  <xsd:complexType>
  ...
  </xsd:complexType>
</xsd:element>

5.4.4. Référence à un élément global

Un élément global, c'est-à-dire dont la déclaration par xsd:element est enfant direct de l'élément xsd:schema, peut être utilisé par des définitions de type. Ces définitions de type se contentent de faire référence à l'élément global de la façon suivante. Ce mécanisme permet une certaine modularité dans l'écriture des schémas puisqu'il évite des répétitions.

<!-- Déclaration globale de l'élément title -->
<xsd:element name="title" type="Title"/>
...
<!-- Définition d'un type global ou local -->
<xsd:complexType ... >
  ...
  <!-- Utilisation de l'élément title -->
  <xsd:element ref="title"/>
  ...
</xsd:complexType>

Les deux attributs name et ref ne peuvent pas être présents simultanément dans l'élément xsd:element. Par contre, l'un des deux doit toujours être présent soit pour donner le nom de l'élément défini soit pour référencer un élément déjà défini.

5.4.5. Éléments locaux

Deux éléments définis non globalement dans un schéma peuvent avoir le même nom tout en ayant des types différents. Il s'agit en fait d'éléments différents mais ayant le même nom. Cette possibilité est absente des DTD où tous les éléments sont globaux et ne peuvent avoir qu'un seul type. Le schéma suivant définit deux éléments de même nom local mais de types xsd:string et xsd:integer. Le premier élément apparaît uniquement dans le contenu de l'élément strings alors que le second apparaît uniquement dans le contenu de l'élément integers. C'est donc le contexte qui permet de distinguer les deux éléments de nom local.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  ...
  <xsd:element name="strings">     
    <xsd:complexType>
      <xsd:sequence>
        <!-- Déclaration du premier élément local -->
        <xsd:element name="local" type="xsd:string" maxOccurs="unbounded"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>    
  <xsd:element name="integers">
    <xsd:complexType>
      <xsd:sequence>
        <!-- Déclaration du second élément local -->
        <xsd:element name="local" type="xsd:integer" maxOccurs="unbounded"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>    
</xsd:schema>

Un document valide pour le schéma précédent est le suivant.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<lists>
  <strings>
    <local>Une chaîne</local>
    <local>A string</local>
  </strings>
  <integers>
    <local>-1</local>
    <local>1</local>
  </integers>
</lists>

D'autres exemples de schémas déclarant plusieurs éléments de même nom mais de types différents sont donnés pour les restrictions de types complexes à contenu complexe.

5.5. Définitions de types

Parmi les types, les schémas XML distinguent les types simples introduits par le constructeur xsd:simpleType et les types complexes introduits par le constructeur xsd:complexType. Les types simples décrivent des contenus textuels, c'est-à-dire ne contenant que du texte. Ils peuvent être utilisés pour les éléments comme pour les attributs. Ils sont généralement obtenus par dérivation des types prédéfinis. Au contraire, les types complexes décrivent des contenus purs constitués uniquement d'éléments ou des contenus mixtes constitués de texte et d'éléments. Ils peuvent uniquement être utilisés pour déclarer des éléments. Seuls les types complexes peuvent définir des attributs.

Les schémas permettent de définir une hiérarchie de types qui sont obtenus par extension ou restriction de types déjà définis. L'extension de type est similaire à l'héritage des langages de programmation orientés objet comme Java ou C++. Elle permet de définir un nouveau type en ajoutant des éléments et/ou des attributs à un type. La restriction permet au contraire d'imposer des contraintes supplémentaires au contenu et aux attributs.

Tous les types prédéfinis ou définis dans un schéma sont dérivés du type xsd:anyType. Ce type est aussi le type par défaut lorsqu'une déclaration d'élément ne spécifie pas le type comme la déclaration suivante.

<!-- Le type de l'élément object est xsd:anyType -->
<xsd:element name="object"/>
Hiérarchie de construction des types

Figure 5.1. Hiérarchie de construction des types


5.5.1. Types prédéfinis

Les schémas possèdent de nombreux types prédéfinis. Certains, comme xsd:int et xsd:float, proviennent des langages de programmation, certains, comme xsd:date et xsd:time, sont inspirés de normes ISO (ISO 8601 dans ce cas) et d'autres encore, comme xsd:ID, sont hérités des DTD. Ces types autorisent l'écriture de schémas concis et très précis. Beaucoup d'entre eux pourraient être redéfinis par restriction de types de base mais leur présence comme types de base simplifie le travail.

5.5.1.1. Types numériques

Beaucoup de types numériques sont prédéfinis pour les nombres entiers et flottants. Certains types comme xsd:int ou xsd:double correspondent à un codage précis et donc à une précision fixée alors que d'autres types comme xsd:integer ou xsd:decimal autorisent une précision arbitraire. Ces derniers types sont à privilégier sauf quand le schéma décrit des données avec un codage bien déterminé.

xsd:boolean

Valeur booléenne avec true ou 1 pour vrai et false ou 0 pour faux

xsd:byte

Nombre entier signé sur 8 bits

xsd:unsignedByte

Nombre entier non signé sur 8 bits

xsd:short

Nombre entier signé sur 16 bits

xsd:unsignedShort

Nombre entier non signé sur 16 bits

xsd:int

Nombre entier signé sur 32 bits

xsd:unsignedInt

Nombre entier non signé sur 32 bits

xsd:long

Nombre entier signé sur 64 bits. Ce type dérive du type xsd:integer.

xsd:unsignedLong

Nombre entier non signé sur 64 bits

xsd:integer

Nombre entier sans limite de précision. Ce type n'est pas primitif et dérive du type xsd:decimal.

xsd:positiveInteger

Nombre entier strictement positif sans limite de précision

xsd:negativeInteger

Nombre entier strictement négatif sans limite de précision

xsd:nonPositiveInteger

Nombre entier négatif ou nul sans limite de précision

xsd:nonNegativeInteger

Nombre entier positif ou nul sans limite de précision

xsd:float

Nombre flottant sur 32 bits conforme à la norme IEEE 754

xsd:double

Nombre flottant sur 64 bits conforme à la norme IEEE 754

xsd:decimal

Nombre décimal sans limite de précision

5.5.1.2. Types pour les chaînes et les noms

Les schémas possèdent bien sûr un type pour les chaînes de caractères ainsi que quelques types pour les noms qualifiés et non qualifiés.

xsd:string

Chaîne de caractères composée de caractères Unicode

xsd:normalizedString

Chaîne de caractères normalisée, c'est-à-dire ne contenant pas de tabulation U+09, de saut de ligne U+0A ou de retour chariot U+0D.

xsd:token

Chaîne de caractères normalisée (comme ci-dessus) et ne contenant pas en outre des espaces en début ou en fin ou des espaces consécutifs

xsd:Name

Nom XML

xsd:QName

Nom qualifié

xsd:NCName

Nom non qualifié, c'est-à-dire sans caractère ':'

xsd:language

Code de langue sur deux lettres de la norme ISO 639 comme fr ou en éventuellement suivi d'un code de pays de la norme ISO 3166 comme en-GB. C'est le type de l'attribut particulier xml:lang auquel il est spécialement destiné.

xsd:anyURI

Un URI comme http://www.omega-one.org/~carton/. C'est le type de l'attribut particulier xml:base.

xsd:base64Binary

Données binaires représentées par une chaîne au format Base 64.

xsd:hexBinary

Données binaires représentées par une chaîne au format Hex.

Les deux types xsd:normalizedString et xsd:token méritent quelques explications. D'une part, il faut bien distinguer le type xsd:token du type xsd:NMTOKEN. Le type xsd:token accepte des valeurs contenant éventuellement des espaces alors que le type xsd:NMTOKEN accepte uniquement un jeton qui ne contient jamais d'espace. D'autre part, les deux types xsd:normalizedString et xsd:token ne restreignent pas les valeurs possibles pour un document valide mais modifient le traitement des caractères d'espacement à l'analyse lexicale. Le type xsd:normalizedString n'interdit pas de mettre des caractères d'espacement autres que des espaces. En revanche, tous les caractères d'espacement sont convertis en espaces par l'analyseur lexical. De la même façon, le type xsd:token n'interdit pas de mettre des caractères d'espacement en début ou en fin ou des caractères d'espacement consécutifs. En revanche, les caractères d'espacement en début ou en fin sont supprimés et les suites de caractères d'espacement sont remplacées par un seul espace par l'analyseur lexical.

5.5.1.3. Types pour les dates et les heures

Quelques types des schémas imposent des formats standards pour écrire les dates et les heures. Ils s'appuient sur la norme ISO 8601. Leur grand intérêt est d'imposer une écriture normalisée pour les dates et les heures et d'en faciliter ainsi le traitement.

xsd:time

Heure au format hh:mm:ss[.sss][TZ], par exemple 14:07:23. La partie fractionnaire .sss des secondes est optionnelle. Tous les autres champs sont obligatoires. Les nombres d'heures, minutes et de secondes doivent être écrits avec deux chiffres en complétant avec 0. L'heure peut être suivie d'un décalage horaire TZ qui est soit Z pour le temps universel soit un décalage commençant par + ou - comme -07:00.

xsd:date

Date au format YYYY-MM-DD, par exemple 2008-01-16. Tous les champs sont obligatoires.

xsd:dateTime

Date et heure au format YYYY-MM-DDThh:mm:ss comme 2008-01-16T14:07:23. Tous les champs sont obligatoires.

xsd:duration

Durée au format PnYnMnDTnHnMnS comme P1Y6M, P1M12DT2H et P1YD3H10S.

xsd:dayTimeDuration

Durée au format PnDTnHnMnS comme P7DT4H3M2S.

xsd:yearMonthDuration

Durée au format PnYnM comme P1Y6M.

xsd:gYear

Année du calendrier grégorien au format YYYY comme 2011.

xsd:gYearMonth

Année et mois du calendrier grégorien au format YYYY-MM comme 1966-06 pour juin 1966.

xsd:gMonth

Mois du calendrier grégorien au format MM comme 01 pour janvier.

xsd:gMonthDay

Jour et mois du calendrier grégorien au format MM-DD comme 12-25 pour le jour de Noël.

xsd:gDay

Jour (dans le mois) du calendrier grégorien au format DD comme 01 pour le premier de chaque mois.

Hiérarchie des types atomiques

Figure 5.2. Hiérarchie des types atomiques


5.5.1.4. Types hérités des DTD

Les schémas XML reprennent certains types des DTD pour les attributs. Ces types facilitent la traduction automatique des DTD en schémas. Pour des raisons de compatibilité, ces types sont réservés aux attributs.

xsd:ID

nom XML identifiant un élément

xsd:IDREF

référence à un élément par son identifiant

xsd:IDREFS

liste de références à des éléments par leurs identifiants

xsd:NMTOKEN

jeton

xsd:NMTOKENS

liste de jetons séparés par des espaces

xsd:ENTITY

entité externe non XML

xsd:ENTITIES

liste d'entités externes non XML séparées par des espaces

xsd:NOTATION

notation

5.5.2. Types simples

Les types simples définissent uniquement des contenus textuels. Ils peuvent être utilisé pour les éléments ou les attributs. Ils sont introduits par l'élément xsd:simpleType. Un type simple est souvent obtenu par restriction d'un autre type défini. Il peut aussi être construit par union d'autres types simples ou par l'opérateur de listes. La déclaration d'un type simple a la forme suivante.

<xsd:simpleType ...>
  ...
</xsd:simpleType>

L'élément xsd:simpleType peut avoir un attribut name si la déclaration est globale. La déclaration du type se fait ensuite dans le contenu de l'élément xsd:simpleType comme dans l'exemple suivant.

<xsd:simpleType name="Byte">
  <xsd:restriction base="xsd:nonNegativeInteger">
    <xsd:maxInclusive value="255"/>
  </xsd:restriction>
</xsd:simpleType>

5.5.3. Types complexes

Les types complexes définissent des contenus purs (constitués uniquement d'éléments), des contenus textuels ou des contenus mixtes. Tous ces contenus peuvent comprendre des attributs. Les types complexes peuvent seulement être utilisés pour les éléments. Ils sont introduits par l'élément xsd:complexType. Un type complexe peut être construit explicitement ou être dérivé d'un autre type par extension ou restriction.

La construction explicite d'un type se fait en utilisant les opérateurs de séquence xsd:sequence, de choix xsd:choice ou d'ensemble xsd:all. La construction du type se fait directement dans le contenu de l'élément xsd:complexType et prend donc la forme suivante.

<!-- Type explicite -->
<xsd:complexType ...>
  <!-- Construction du type avec xsd:sequence, xsd:choice ou xsd:all -->
  ...
</xsd:complexType>

Si le type est obtenu par extension ou restriction d'un autre type, l'élément xsd:complexType doit contenir un élément xsd:simpleContent ou xsd:complexContent qui précise si le contenu est purement textuel ou non. La déclaration d'un type complexe prend alors une des deux formes suivantes.

<!-- Type dérivé à contenu textuel -->
<xsd:complexType ...>
  <xsd:simpleContent>
    <!-- Extension ou restriction -->
    ...
  </xsd:simpleContent>
</xsd:complexType>
<!-- Type dérivé à contenu pur ou mixte -->
<xsd:complexType ...>
  <xsd:complexContent>
    <!-- Extension ou restriction -->
    ...
  </xsd:complexContent>
</xsd:complexType>

5.5.4. Contenu mixte

Le contenu d'un élément est pur lorsqu'il ne contient que des éléments qui, eux-mêmes, peuvent à leur tour contenir du texte et/ou des éléments. Il est, au contraire, mixte lorsqu'il contient du texte autre que des caractères d'espacement en dehors de ses enfants. La construction de types pour les contenus mixtes est très facile et très souple avec les schémas. L'attribut mixed de l'élément xsd:complexType permet de construire un type avec du contenu mixte. Il faut, pour cela, lui donner la valeur true.

Le contenu d'un élément est valide pour un type déclaré mixte si le contenu devient valide pour le type non mixte correspondant lorsque tout le texte en dehors des enfants est supprimé. Dans l'exemple suivant, l'élément person doit contenir, un élément firstname et un élément lastname dans cet ordre. Puisque son type est déclaré mixte, il peut en outre contenir du texte comme ci-dessous.

<xsd:element name="person">     
  <xsd:complexType mixed="true">
    <xsd:sequence>
      <xsd:element name="firstname" type="xsd:string"/>
      <xsd:element name="lastname"  type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

Le document suivant est valide pour le schéma précédent. En revanche, il ne le serait pas sans la valeur true donnée à l'attribut mixed dans le schéma. Le contenu de l'élément person est valide car si on retire le texte en dehors de ses enfants firstname et lastname (texte en gras ci-dessous), on obtient un contenu valide pour le type sans mixed="true". Il serait, par exemple, impossible de changer l'ordre des enfants firstname et lastname car ce type spécifie qu'ils doivent apparaître dans cet ordre.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<person>
  Prénom : <firstname>Albert</firstname>,
  Nom : <lastname>Einstein</lastname>.
</person>

Le schéma précédent n'a pas d'équivalent dans les DTD. Dès que le contenu d'un élément est déclaré mixte dans une DTD, il n'y a plus aucun contrôle sur l'ordre et le nombre d'occurrences de ses enfants. Le schéma suivant donne un exemple de contenu mixte équivalent à un contenu mixte d'une DTD.

<xsd:element name="book"> 
  <xsd:complexType mixed="true">
    <xsd:choice minOccurs="0" maxOccurs="unbounded">
      <xsd:element ref="em"/>
      <xsd:element ref="cite"/>
    </xsd:choice>
  </xsd:complexType>
</xsd:element>

L'exemple précédent est équivalent au fragment suivant de DTD.

<!ELEMENT book (#PCDATA | em | cite)* >

5.6. Constructions de types

Les constructeurs de types permettent de définir des nouveaux types en combinant des types déjà définis. Ils sont en fait assez semblables aux différents opérateurs des DTD.

5.6.1. Élément vide

Si un type complexe déclare uniquement des attributs, le contenu de l'élément doit être vide. Par exemple, le type suivant déclare un type Link. Tout élément de ce type doit avoir un contenu vide et un attribut ref de type xsd:IDREF. Il s'agit en fait d'une extension du type vide par ajout d'attributs.

<xsd:element name="link" type="Link"/>
<xsd:complexType name="Link">
  <xsd:attribute name="ref" type="xsd:IDREF" use="required"/>
</xsd:complexType>

Le fragment de document suivant est valide pour le schéma précédent.

<link ref="id-42"/>

5.6.2. Opérateur de séquence

L'opérateur xsd:sequence définit un nouveau type formé d'une suite des éléments énumérés. C'est l'équivalent de l'opérateur ',' des DTD. Les éléments énumérés peuvent être soit des éléments explicites, soit des éléments référencés avec l'attribut ref soit des types construits récursivement avec les autres opérateurs. Dans l'exemple suivant, un élément book doit contenir les éléments title, author, year et publisher dans cet ordre.

<xsd:element name="book">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="title"     type="xsd:string"/>
      <xsd:element name="author"    type="xsd:string"/>
      <xsd:element name="year"      type="xsd:string"/>
      <xsd:element name="publisher" type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

Cette déclaration est équivalente à la déclaration suivante dans une DTD.

<!ELEMENT book (title, author, year, publisher)>

Le fragment de document ci-dessous est valide pour la déclaration de l'élément book.

<book>
  <title>XML langage et applications</title>
  <author>Alain Michard</author>
  <year>2001</year>
  <publisher>Eyrolles</publisher>
</book>

Un autre exemple de type construit avec l'opérateur xsd:sequence est donné comme exemple de type mixte. Le nombre d'occurences de chaque élément dans la séquence est 1 par défaut mais il peut être modifié par les attributs minOccurs et maxOccurs.

5.6.3. Opérateur de choix

L'opérateur xsd:choice définit un nouveau type formé d'un des éléments énumérés. C'est l'équivalent de l'opérateur '|' des DTD. Dans l'exemple suivant, le contenu de l'élément publication doit être un des éléments book, article ou report. Ces trois éléments sont référencés et doivent donc être définis globalement dans le schéma.

<xsd:element name="publication">
  <xsd:complexType>
    <xsd:choice>
      <xsd:element ref="book"/>
      <xsd:element ref="article"/>
      <xsd:element ref="report"/>
    </xsd:choice>
  </xsd:complexType>
</xsd:element>

Cette déclaration est équivalente à la déclaration suivante dans une DTD.

<!ELEMENT publication (book | article | report)>

Un autre exemple de type construit avec l'opérateur xsd:choice est donné comme exemple de type mixte. Il est bien sûr possible d'imbriquer les opérateurs xsd:sequence et xsd:choice. Dans l'exemple suivant, l'élément book contient soit un seul élément author soit un élément authors contenant au moins deux éléments author. Cette dernière contrainte est imposée par la valeur 2 de l'attribut minOccurs.

<xsd:element name="book" minOccurs="1" maxOccurs="unbounded">
  <xsd:complexType>
  <xsd:sequence>
    <xsd:element name="title" type="xsd:string"/>
    <xsd:choice>
      <xsd:element name="author" type="xsd:string"/>
      <xsd:element name="authors">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="author" type="xsd:string" 
                         minOccurs="2" maxOccurs="unbounded"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:choice>
    <xsd:element name="year" type="xsd:string"/>
    ...
  </xsd:sequence>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1"?>
<bibliography>
  <!-- Livre à un seul auteur -->
  <book key="Michard01" lang="fr">
    <title>XML langage et applications</title>
    <author>Alain Michard</author>
    <year>2001</year>
    <publisher>Eyrolles</publisher>
  </book>
  <!-- Livre à deux auteurs -->
  <book key="ChagnonNolot07" lang="fr">
    <title>XML</title>
    <authors>
      <author>Gilles Chagnon</author>
      <author>Florent Nolot</author>
    </authors>
    <year>2007</year>
    <publisher>Pearson</publisher>
  </book>
</bibliography>

5.6.4. Opérateur d'ensemble

L'opérateur xsd:all n'a pas d'équivalent dans les DTD. Il définit un nouveau type dont chacun des éléments doit apparaître une fois dans un ordre quelconque. Dans la déclaration ci-dessous, les éléments contenus dans l'élément book peuvent apparaître dans n'importe quel ordre.

<xsd:element name="book">
  <xsd:complexType>
    <xsd:all>
      <xsd:element name="title"     type="xsd:string"/>
      <xsd:element name="author"    type="xsd:string"/>
      <xsd:element name="year"      type="xsd:string"/>
      <xsd:element name="publisher" type="xsd:string"/>
    </xsd:all>
  </xsd:complexType>
</xsd:element>

Le document suivant est un document valide où l'élément book a la déclaration précédente. L'ordre des enfants de l'élément book peut être variable.

<?xml version="1.0" encoding="iso-8859-1"?>
<bibliography>
  <book key="Michard01" lang="fr">
    <author>Alain Michard</author>
    <title>XML langage et applications</title>
    <publisher>Eyrolles</publisher>
    <year>2001</year>
  </book>
  <book key="Zeldman03" lang="en">
    <title>Designing with web standards</title>
    <author>Jeffrey Zeldman</author>
    <year>2003</year>
    <publisher>New Riders</publisher>
  </book>
  ...
</bibliography>

L'utilisation de l'élément xsd:all doit respecter quelques contraintes qui limitent fortement son intérêt. Un opérateur xsd:all ne peut pas être imbriqué avec d'autres constructeurs xsd:sequence, xsd:choice ou même xsd:all. D'une part, les seuls enfants possibles de xsd:all sont des éléments xsd:element. D'autre part, l'élément xsd:all est toujours enfant de xsd:complexType ou xsd:complexContent.

Les attributs minOccurs et maxOccurs des éléments apparaissant sous l'opérateur xsd:all ne peuvent pas avoir des valeurs quelconques. La valeur de l'attribut minOccurs doit être 0 ou 1 et la valeur de l'attribut maxOccurs doit être 1 qui est la valeur par défaut. Les attributs minOccurs et maxOccurs peuvent aussi apparaître comme attribut de xsd:all. Dans ce cas, leurs valeurs s'appliquent à tous les éléments xsd:element enfants de xsd:all. Les valeurs autorisées pour minOccurs sont 0 et 1 et la seule valeur autorisée pour maxOccurs est 1.

Dans la déclaration suivante de l'élément book, ses enfants peuvent apparaître dans n'importe quel ordre et chacun d'eux peut avoir 0 ou 1 occurrence.

<xsd:element name="book">
  <xsd:complexType>
    <xsd:all minOccurs="0">
      <xsd:element name="title"     type="xsd:string"/>
      <xsd:element name="author"    type="xsd:string"/>
      <xsd:element name="year"      type="xsd:string"/>
      <xsd:element name="publisher" type="xsd:string"/>
    </xsd:all>
  </xsd:complexType>
</xsd:element>

5.6.5. Opérateur d'union

L'opérateur xsd:union définit un nouveau type simple dont les valeurs sont celles des types listés dans l'attribut memberTypes.

Le type IntegerOrUnbounded défini ci-dessous pourrait être le type de l'attribut maxOccurs tel qu'il pourrait apparaître dans un schéma pour les schémas. Les valeurs de ce type sont soit un entier positif ou nul du type xsd:nonNegativeInteger soit l'unique chaîne unbounded du type Unbounded. Ce dernier type est obtenu par restriction du type xsd:string.

<xsd:attribute name="maxOccurs" type="IntegerOrUnbounded"/>
<xsd:simpleType name="IntegerOrUnbounded">
  <xsd:union memberTypes="xsd:nonNegativeInteger Unbounded"/>
</xsd:simpleType>
<xsd:simpleType name="Unbounded">
  <xsd:restriction base="xsd:string">
    <xsd:enumeration value="unbounded"/>
  </xsd:restriction>
</xsd:simpleType>

Les types paramètres de l'opérateur d'union peuvent aussi être anonymes. Ils sont alors explicités directement dans le contenu de l'élément xsd:union comme dans l'exemple suivant qui conduit à une définition équivalence à celle de l'exemple précédent.

<xsd:simpleType name="IntegerOrUnbounded">
  <xsd:union memberTypes="xsd:nonNegativeInteger">
    <xsd:simpleType>
      <xsd:restriction base="xsd:string">
        <xsd:enumeration value="unbounded"/>
      </xsd:restriction>
    </xsd:simpleType>
  </xsd:union>
</xsd:simpleType>

5.6.6. Opérateur de liste

L'opérateur xsd:list définit un nouveau type simple dont les valeurs sont les listes de valeurs du type simple donné par l'attribut itemType. Il ne s'agit pas de listes générales comme dans certains langages de programmation. Il s'agit uniquement de listes de valeurs séparées par des espaces. Ces listes sont souvent utilisées comme valeurs d'attributs. Les valeurs du type simple donné par itemType ne peuvent pas comporter de caractères d'espacement qui perturberaient la séparation entre les valeurs. L'exemple suivant définit des types pour les listes d'entiers et pour les listes de 5 entiers.

<!-- Type pour les listes d'entiers -->
<xsd:simpleType name="IntList">
  <xsd:list itemType="xsd:integer"/>
</xsd:simpleType>
<!-- Type pour les listes de 5 entiers -->
<xsd:simpleType name="IntList5">
  <xsd:restriction base="IntList">
    <xsd:length value="5"/>
  </xsd:restriction>
</xsd:simpleType>

5.6.7. Répétitions

Les attributs minOccurs et maxOccurs permettent de préciser le nombre minimal ou maximal d'occurrences d'un élément ou d'un groupe. Ils sont l'équivalent des opérateurs ?, * et + des DTD. Ils peuvent apparaître comme attribut des éléments xsd:element, xsd:sequence, xsd:choice et xsd:all. L'attribut minOccurs prend un entier comme valeur. L'attribut maxOccurs prend un entier ou la chaîne unbounded comme valeur pour indiquer qu'il n'y a pas de nombre maximal. La valeur par défaut de ces deux attributs est la valeur 1.

L'utilisation des attributs minOccurs et maxOccurs est illustrée par l'équivalent en schéma de quelques fragments de DTD

<!ELEMENT elem (elem1, elem2?, elem3*)>

<xsd:element name="elem">
 <xsd:complexType>
   <xsd:sequence>
     <xsd:element ref="elem1"/>
     <xsd:element ref="elem2" minOccurs="0"/>
     <xsd:element ref="elem3" minOccurs="0" maxOccurs="unbounded"/>
   </xsd:sequence>
 </xsd:complexType>
</xsd:element>

<!ELEMENT elem (elem1, (elem2 | elem3)?, elem4)>

<xsd:element name="elem">
 <xsd:complexType>
   <xsd:sequence>
     <xsd:element ref="elem1"/>
     <xsd:choice minOccurs="0">
       <xsd:element ref="elem2"/>
       <xsd:element ref="elem3"/>
     </xsd:choice>
   <xsd:element ref="elem4"/>
   </xsd:sequence>
 </xsd:complexType>
</xsd:element>

<!ELEMENT elem (elem1, elem2, elem3)*>

<xsd:element name="elem">
 <xsd:complexType>
   <xsd:sequence minOccurs="0" maxOccurs="unbounded">
     <xsd:element ref="elem1"/>
     <xsd:element ref="elem2"/>
     <xsd:element ref="elem3"/>
   </xsd:sequence>
 </xsd:complexType>
</xsd:element>

<!ELEMENT elem (elem1 | elem2 | elem3)*>

<xsd:element name="elem">
 <xsd:complexType>
   <xsd:choice minOccurs="0" maxOccurs="unbounded">
     <xsd:element ref="elem1"/>
     <xsd:element ref="elem2"/>
     <xsd:element ref="elem3"/>
   </xsd:choice>
 </xsd:complexType>
</xsd:element>

<!ELEMENT elem (elem1, elem2, elem3)+>

<xsd:element name="elem">
 <xsd:complexType>
   <xsd:sequence minOccurs="1" maxOccurs="unbounded">
     <xsd:element ref="elem1"/>
     <xsd:element ref="elem2"/>
     <xsd:element ref="elem3"/>
   </xsd:sequence>
 </xsd:complexType>
</xsd:element>

5.6.8. Joker xsd:any

L'opérateur xsd:any permet d'introduire dans un document un ou des éléments externes au schéma, c'est-à-dire non définis dans le schéma. Le nombre d'éléments externes autorisés peut-être spécifié avec les attributs minOccurs et maxOccurs. La validation de ces éléments externes est contrôlée par l'attribut processContents qui peut prendre les valeurs strict, lax et skip. La valeur par défaut est strict. Lorsque la valeur est strict, les éléments externes doivent être validés par un autre schéma déterminé par l'espace de noms de ces éléments pour que le document global soit valide. Lorsque la valeur est skip, les éléments externes ne sont pas validés. La valeur lax est intermédiaire entre strict et skip. La validation des éléments externes est tentée mais elle peut échouer.

Le schéma suivant autorise zéro ou un élément externe dans le contenu de l'élément person après l'élément lastname.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="person">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="firstname" type="xsd:string"/>
        <xsd:element name="lastname"  type="xsd:string"/>
        <xsd:any processContents="lax" minOccurs="0"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="utf-8"?>
<person>
  <firstname>Elizabeth II Alexandra Mary</firstname>
  <lastname>Windsor</lastname>
  <title>Queen of England</title>
</person>

L'attribut namespace de l'élément xsd:any permet de préciser les espaces de noms auxquels doivent appartenir les éléments externes. Sa valeur doit être une suite d'URI identifiant des espaces de noms séparés par des espaces. Les valeurs particulières ##any, ##other, ##local et ##targetNamepspace peuvent aussi apparaître dans la liste des espaces de noms. La valeur par défaut ##any n'impose aucune restriction sur l'espace de noms des éléments externes. Les valeurs ##targetNamepspace et ##other autorisent respectivement des éléments dont l'espace de noms est égal ou différent de l'espace de noms cible du schéma. La valeur ##local autorise des éléments ayant des noms non qualifiés et n'appartenant donc à aucun espace de noms.

Dans le schéma suivant, l'élément externe ajouté dans le contenu de l'élément person doit appartenir à l'espace de noms XHTML.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="person">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="firstname" type="xsd:string"/>
        <xsd:element name="lastname"  type="xsd:string"/>
        <xsd:any processContents="lax" namespace="http://www.w3.org/1999/xhtml"
                 minOccurs="0"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="utf-8"?>
<person>
  <firstname>Victor</firstname>
  <lastname>Hugo</lastname>
  <html:ul xmlns:html="http://www.w3.org/1999/xhtml">
    <html:li>Romancier</html:li>
    <html:li>Poète</html:li>
    <html:li>Dramaturge</html:li>
  </html:ul>
</person>

Il existe également un élément xsd:anyAttribute pour autoriser des attributs externes au schéma.

5.6.9. Validation

Cette section aborde deux aspects techniques de la validation du contenu d'éléments purs. Ces deux aspects sont la présence des caractères d'espacement et le déterminisme des contenus. Ils ont déjà été abordés pour les DTD.

Lorsque le contenu d'un élément d'un élément pur est validé, les caractères d'espacement qui se trouvent hors de ses enfants sont ignorés. Ceci autorise l'indentation des documents XML sans perturber la validation.

Le contenu des éléments purs et mixtes doit être déterministe. Ceci signifie que la présence d'une balise ouvrante dans le contenu doit complètement déterminer d'où provient celle-ci dans la définition du type. Cette restriction est identique à celle portant sur les expressions définissant le contenu d'un élément pur dans une DTD. Le schéma suivant n'est pas valable car le contenu de l'élément item n'est pas déterministe. Une balise ouvrante <item1> peut provenir de la première ou de la seconde déclaration d'élément item1.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="item">
    <xsd:complexType>
      <xsd:choice>
        <xsd:sequence>
          <xsd:element name="item1" type="xsd:string"/>
          <xsd:element name="item2" type="xsd:string"/>
        </xsd:sequence>
        <xsd:sequence>
          <xsd:element name="item1" type="xsd:string"/>
          <xsd:element name="item3" type="xsd:string"/>
        </xsd:sequence>
      </xsd:choice>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

5.7. Déclarations d'attributs

La déclaration d'un attribut est semblable à la déclaration d'un élément mais elle utilise l'élément xsd:attribute au lieu de l'élément xsd:element. Les attributs name et type de xsd:attribute spécifient respectivement le nom et le type de l'attribut. Le type d'un attribut est nécessairement un type simple puisque les attributs ne peuvent contenir que du texte. La déclaration d'un attribut prend la forme suivante.

<xsd:attribute name="name" type="type"/>

L'exemple suivant déclare un attribut format de type xsd:string.

<xsd:attribute name="format" type="xsd:string"/>

Comme pour un élément, le type d'un attribut peut être anonyme. Il est alors défini dans le contenu de l'élément xsd:attribute. Cette possibilité est illustrée par l'exemple suivant. La valeur de l'attribut lang déclaré ci-dessous peut être la chaîne en ou la chaîne fr.

<xsd:attribute name="lang">
  <xsd:simpleType>
    <xsd:restriction base="xsd:string">
      <xsd:enumeration value="en"/>
      <xsd:enumeration value="fr"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:attribute>

5.7.1. Localisation

Les déclarations d'attributs se placent normalement dans les définitions de types complexes qui peuvent être globaux ou locaux. Les types simples ne peuvent pas avoir d'attributs. La définition d'un type complexe se compose de la description du contenu suivie de la déclaration des attributs. L'ordre de déclarations des attributs est sans importance puisque l'ordre des attributs dans une balise n'est pas fixe.

Dans l'exemple suivant, le type complexe List déclare deux attributs form et lang. Les déclarations de ces deux attributs se situent après la description du contenu du type List constitué d'une suite d'éléments item. Le type de l'attribut form est le type prédéfini xsd:string alors que le type de l'attribut lang est le type global et simple Lang défini dans la suite du schéma.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:complexType name="List">
    <!-- Contenu du type List -->
    <xsd:sequence maxOccurs="unbounded">
      <xsd:element name="item" type="xsd:string"/>
    </xsd:sequence>
    <!-- Déclaration des attributs locaux form et lang du type List -->
    <xsd:attribute name="form" type="xsd:string"/>
    <xsd:attribute name="lang" type="Lang"/>
  </xsd:complexType>
  <!-- Type global et simple Lang pour l'attribut lang -->
  <xsd:simpleType name="Lang">
    <xsd:restriction base="xsd:string">
      <xsd:length value="2"/>
    </xsd:restriction>
  </xsd:simpleType>
  <xsd:element name="list" type="List"/>
</xsd:schema>

Le fragment de document suivant est valide pour le schéma précédent.

<list form="long" lang="en">
  <item>Item 1</item>
  <item>Item 2</item>
</list>

Un attribut peut aussi être global lorsque sa déclaration par xsd:attribute est enfant direct de l'élément xsd:schema. Cet attribut peut alors être ajouté à différents types complexes. La définition du type utilise l'élément xsd:attribute avec un attribut ref qui remplace les deux attributs name et type. Cet attribut ref contient le nom de l'attribut global à ajouter. La déclaration globale d'un attribut est justifiée lorsque celui-ci a des occurrences multiples. Elle accroît la modularité des schémas en évitant de répéter la même déclaration dans plusieurs types.

Le schéma suivant déclare un attribut global lang de type xsd:language. Cet attribut est ajouté à deux reprises dans le type global Texts et dans le type anonyme de l'élément text.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Déclaration de l'attribut global lang -->
  <xsd:attribute name="lang"  type="xsd:language"/>
  <xsd:element   name="texts" type="Texts"/>
  <xsd:complexType name="Texts">
    <xsd:sequence>
      <xsd:element name="text" maxOccurs="unbounded">
        <xsd:complexType>
          <xsd:simpleContent>
            <xsd:extension base="xsd:string">
              <!-- Ajout de l'attribut lang au type anonyme -->
              <xsd:attribute ref="lang"/>
            </xsd:extension>
          </xsd:simpleContent>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
    <!-- Ajout de l'attribut lang au type Texts -->
    <xsd:attribute ref="lang"/>
  </xsd:complexType>
</xsd:schema>

Lorsqu'un schéma déclare un espace de noms cible, les attributs globaux appartiennent automatiquement à cet espace de noms. Ceci signifie d'abord qu'ils doivent être référencés par leur nom qualifié dans le schéma. L'ajout d'un attribut de nom name à un type complexe prend alors la forme suivante où le préfixe tns est associé à l'espace de noms cible du schéma.

<xsd:attribute ref="tns:name"/>

Cela signifie aussi qu'ils doivent avoir un nom qualifié dans les documents instance comme dans l'exemple suivant.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<tns:texts tns:lang="fr" xmlns:tns="http://www.omega-one.org/~carton/">
  <text>Texte en français</text>
  <text tns:lang="en">Text in english</text>
</tns:texts>

5.7.2. Attribut optionnel, obligatoire ou interdit

Par défaut, un attribut est optionnel. Il peut être présent ou absent. Il peut aussi être rendu obligatoire ou interdit en donnant la valeur required ou prohibited à l'attribut use de l'élément xsd:attribute. L'attribut use peut aussi prendre la valeur optional. Cette valeur est très peu utilisée car c'est la valeur par défaut. La valeur prohibited est utile dans les restrictions de types pour modifier l'utilisation d'un attribut.

Les valeurs optional et required de l'attribut use sont donc équivalentes à #IMPLIED et #REQUIRED utilisés dans les déclarations d'attributs des DTD. Dans l'exemple suivant, les attributs lang, xml:id et dummy sont respecivement optionnel, obligatoire et interdit.

<xsd:attribute name="lang"   type="xsd:NMTOKEN" use="optional"/>
<xsd:attribute name="xml:id" type="xsd:ID"      use="required"/>
<xsd:attribute name="dummy"  type="xsd:string"  use="prohibited"/>

Le schéma suivant donne une utilisation réaliste de la valeur prohibited pour l'attribut use. Le type Date déclare un attribut format optionnel. Le type Date-8601 est une restriction du type Date. L'attribut format devient interdit et ne peut plus apparaître.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:complexType name="Date">
    <xsd:simpleContent>
      <xsd:extension base="xsd:date">
        <!-- Attribut format optionnel dans le type Date -->
        <xsd:attribute name="format" type="xsd:string"/>
      </xsd:extension>
    </xsd:simpleContent>
  </xsd:complexType>
  <xsd:complexType name="Date-8601">
    <xsd:simpleContent>
      <xsd:restriction base="Date">
        <!-- Attribut format interdit dans le type Date-8601 -->
        <xsd:attribute name="format" type="xsd:string" use="prohibited"/>
      </xsd:restriction>
    </xsd:simpleContent>
  </xsd:complexType>
  <xsd:element name="date" type="Date-8601"/>
</xsd:schema>
<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:complexType name="Date">
    <xsd:simpleContent>
      <xsd:extension base="xsd:date">
        <!-- Attribut format optionnel dans le type Date -->
        <xsd:attribute name="format" type="xsd:string"/>
      </xsd:extension>
    </xsd:simpleContent>
  </xsd:complexType>
  <xsd:complexType name="Date-8601">
    <xsd:simpleContent>
      <xsd:restriction base="Date">
        <!-- Attribut format interdit dans le type Date-8601 -->
        <xsd:attribute name="format" type="xsd:string" use="prohibited"/>
      </xsd:restriction>
    </xsd:simpleContent>
  </xsd:complexType>
  <xsd:element name="date" type="Date-8601"/>
</xsd:schema>

Le fragment de document suivant est valide pour ce schéma. L'attribut format ne peut pas être présent dans l'élément date.

<date format="iso-8601"/></date>

5.7.3. Valeur par défaut et valeur fixe

Comme pour les éléments, il est possible de donner une valeur par défaut ou une valeur fixe à un attribut. La valeur de l'attribut default ou de l'attribut fixed de l'élément xsd:attribute permet de spécifier cette valeur. Il va de soi qu'une valeur par défaut n'est autorisée que si l'attribut est optionnel. Il est également interdit de donner simultanément une valeur par défaut et une valeur fixe. L'attribut fixed est équivalent à #FIXED utilisé dans les déclarations d'attribut des DTD. Dans le premier exemple suivant, la valeur par défaut de l'attribut lang est fr et dans le second exemple, sa valeur est fixée à la valeur en.

<xsd:attribute name="lang" type="xsd:NMTOKEN" default="fr"/>
<xsd:attribute name="lang" type="xsd:NMTOKEN" fixed="en"/>

5.7.4. Joker xsd:anyAttribute

De même qu'il existe un élément xsd:any pour autoriser des éléments externes, il existe aussi un élément xsd:anyAttribute pour autoriser des attributs externes au schéma. Il possède également les attributs processContents et namespace pour contrôler la validation et l'espace de noms des attributs externes ajoutés dans les documents.

Dans le schéma suivant, l'élément person peut avoir des attributs appartenant à l'espace de noms identifié par l'URI http://www.omega-one.org/~carton/.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="person">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="firstname" type="xsd:string"/>
        <xsd:element name="lastname"  type="xsd:string"/>
      </xsd:sequence>
      <xsd:anyAttribute processContents="lax"
                        namespace="http://www.omega-one.org/~carton/"/>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="utf-8"?>
<person tns:id="id42" xmlns:tns="http://www.omega-one.org/~carton/">
  <firstname>Victor</firstname>
  <lastname>Hugo</lastname>
</person>

5.8. Extension de types

L'extension est la première façon d'obtenir un type dérivé à partir d'un type de base. L'idée générale de l'extension est de rajouter du contenu et des attributs. Elle s'apparente à la dérivation de types des langages de programmation orientés objet comme Java ou C++. Les contenus du type dérivé ne sont généralement pas valides pour le type de base car des éléments et/ou des attributs nouveaux peuvent apparaître. L'extension s'applique aux types simples et aux types complexes mais elle donne toujours un type complexe.

L'extension d'un type est introduite par l'élément xsd:extension dont l'attribut base donne le nom du type de base. Celui-ci peut être un type prédéfini ou un type défini dans le schéma. Le contenu de l'élément xsd:extension explicite le contenu et les attributs à ajouter au type de base. L'élément xsd:extension est enfant d'un élément xsd:simpleContent ou xsd:complexContent, lui-même enfant de l'élément xsd:complexType.

5.8.1. Types simples

L'extension d'un type simple ne permet pas de changer le contenu mais permet uniquement d'ajouter des attributs pour donner un type complexe à contenu simple. C'est en fait la seule façon d'obtenir un tel type s'il on exclut l'extension ou la restriction d'un type qui est déjà dans cette catégorie. Lors d'une extension d'un type simple, l'élément xsd:extension est toujours enfant d'un élément xsd:simpleContent. Les déclarations des attributs qui sont ajoutés sont placées dans le contenu de l'élément xsd:extension.

Le fragment de schéma suivant définit un type Price qui étend le type prédéfini xsd:decimal en lui ajoutant un attribut currency de type xsd:string

<xsd:complexType name="Price">
  <xsd:simpleContent>
    <xsd:extension base="xsd:decimal">
      <!-- Attribut ajouté -->
      <xsd:attribute name="currency" type="xsd:string"/>
    </xsd:extension>
  </xsd:simpleContent>
</xsd:complexType>
<xsd:element name="price" type="Price"/>

Le fragment de document suivant est valide pour le schéma précédent.

<price currency="euro">3.14</price>

5.8.2. Types complexes à contenu simple

Il est possible d'étendre un type complexe à contenu simple pour lui ajouter de nouveaux attributs. On obtient alors un nouveau type complexe à contenu simple qui possède les attributs du type de base et ceux déclarés par l'extension. L'extension d'un tel type est similaire à l'extension d'un type simple. L'élément xsd:extension est encore enfant d'un élément xsd:simpleContent. Les déclarations des attributs qui sont ajoutés sont placées dans le contenu de l'élément xsd:extension.

Dans le schéma suivant, le type Price est étendu en un type LocalType qui possède un nouvel attribut country de type xsd:string.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Type de base complexe à contenu simple -->
  <xsd:complexType name="Price">
    <xsd:simpleContent>
      <xsd:extension base="xsd:decimal">
        <!-- Attribut ajouté au type xsd:decimal -->
        <xsd:attribute name="currency" type="xsd:string"/>
      </xsd:extension>
    </xsd:simpleContent>
  </xsd:complexType>
  <!-- Extension du type de base -->
  <xsd:complexType name="LocalPrice">
    <xsd:simpleContent>
      <xsd:extension base="Price">
        <!-- Attribut ajouté au type Price -->
        <xsd:attribute name="country" type="xsd:string"/>
      </xsd:extension>
    </xsd:simpleContent>
  </xsd:complexType>
  <xsd:element name="price" type="LocalPrice"/>
</xsd:schema>

Le fragment de document suivant est valide pour le schéma précédent. Comme l'ordre des attributs est sans importance, l'attribut country aurait pu être placé avant l'attribut currency.

<price currency="euro" country="France">3.14</price>

5.8.3. Types complexes à contenu complexe

L'extension d'un type complexe à contenu complexe consiste à ajouter du contenu et/ou des attributs. Le contenu est ajouté après le contenu du type de base. L'ajout d'attribut est semblable au cas des types complexes à contenu simple.

Dans le schéma suivant le type Fullname étend le type Name en lui ajoutant un élément title et un attribut id.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Type de base -->
  <xsd:complexType name="Name">
    <xsd:sequence>
      <xsd:element name="firstname" type="xsd:string"/>
      <xsd:element name="lastname"  type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
  <!-- Extension du type de base -->
  <xsd:complexType name="Fullname">
    <xsd:complexContent>
      <xsd:extension base="Name">
        <xsd:sequence>
          <!-- Ajout de l'élément title après firstname et lastname -->
          <xsd:element name="title" type="xsd:string"/>
        </xsd:sequence>
        <!-- Ajout de l'attribut id -->
        <xsd:attribute name="id" type="xsd:ID"/>
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>
  ...
</xsd:schema>

L'élément title est ajouté après le contenu du type Name qui est constitué des deux éléments firstname et lastname. Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
<names>
  <fullname id="id40">
    <firstname>Alexander III Alexandrovich</firstname>
    <lastname>Romanov</lastname>
    <title>Tsar of Russia</title>
  </fullname>
  <fullname id="id52">
    <firstname>Elizabeth II Alexandra Mary</firstname>
    <lastname>Windsor</lastname>
    <title>Queen of England</title>
  </fullname>
</names>

5.9. Restriction de types

La restriction est la deuxième façon d'obtenir un type dérivé à partir d'un type de base. L'idée générale de la restriction est de définir un nouveau type dont les contenus au sens large sont des contenus du type de base. Par contenus au sens large, on entend les contenus proprement dits ainsi que les valeurs des attributs. La restriction s'applique aux types simples et aux types complexes mais elle prend des formes différentes suivant les cas.

La restriction d'un type est introduite par l'élément xsd:restriction dont l'attribut base donne le nom du type de base. Celui-ci peut être un type prédéfini ou un type défini dans le schéma. Le contenu de l'élément xsd:restriction explicite les restrictions au type de base. Dans le cas d'un type simple, l'élément xsd:restriction est enfant direct de l'élément xsd:simpleType. Dans le cas d'un type complexe, il est enfant d'un élément xsd:simpleContent ou xsd:complexContent, lui-même enfant de l'élément xsd:complexType.

5.9.1. Types simples

Les schémas définissent un certain nombre de types de base. Tous les autres types simples sont obtenus par restriction directe ou multiple de ces différents types de base. La restriction des types simples est effectuée par l'intermédiaire de facettes qui imposent des contraintes aux contenus. Toutes les facettes ne s'appliquent pas à tous les types simples. On donne d'abord quelques exemples de restrictions classiques à l'aide des principales facettes puis une liste exhaustive des facettes.

5.9.1.1. Restriction par intervalle

Il est possible de restreindre les contenus en donnant une valeur minimale et/ou une valeur maximale avec les éléments xsd:minInclusive, xsd:minExclusive, xsd:maxInclusive et xsd:maxExclusive. Ces contraintes s'appliquent aux types numériques pour lesquels elles ont un sens. Dans l'exemple suivant, le type donné à l'élément year est un entier entre 1970 et 2050 inclus. Le type utilisé dans cet exemple est un type anonyme.

<xsd:element name="year"> 
  <xsd:simpleType>
    <xsd:restriction base="xsd:integer">
      <xsd:minInclusive value="1970"/>
      <xsd:maxInclusive value="2050"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:element>

Un exemple de contenu valide pour l'élément year est donné ci-dessous.

<year>1966</year>

La restriction par intervalle peut aussi s'appliquer aux dates et aux heures comme le montre l'exemple suivant.

<xsd:attribute name="date">
  <xsd:simpleType>
    <xsd:restriction base="xsd:date">
      <!-- Date après le 31 décembre 2001 exclus -->
      <xsd:minExclusive value="2001-12-31"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:attribute>

Un exemple de valeur valide pour l'attribut date est donné ci-dessous.

<event date="2006-06-24">...</event>

5.9.1.2. Restriction par énumération

Il est possible de donner explicitement une liste des valeurs possibles d'un type prédéfini ou déjà défini avec l'élément xsd:enumeration. Dans l'exemple suivant, le type donné à l'élément language comprend uniquement les trois chaînes de caractères de, en et fr. Le type utilisé est un type nommé Language.

<xsd:element name="language" type="Language"/> 
<xsd:simpleType name="Language">
  <xsd:restriction base="xsd:language">
    <xsd:enumeration value="de"/>
    <xsd:enumeration value="en"/>
    <xsd:enumeration value="fr"/>
  </xsd:restriction>
</xsd:simpleType>

Un exemple de valeur valide pour l'attribut date est donné ci-dessous.

<language>en</language>

5.9.1.3. Restriction par motif

Il est possible de restreindre les valeurs en donnant, avec l'élément xsd:pattern, une expression rationnelle qui décrit les valeurs possibles d'un type prédéfini ou déjà défini. Le contenu est valide s'il est conforme à l'expression rationnelle. Dans l'exemple suivant, le type ISBN décrit explicitement toutes les formes possibles des numéros ISBN à 10 chiffres.

<xsd:simpleType name="ISBN">
  <xsd:restriction base="xsd:string">
    <xsd:pattern value="\d-\d{2}-\d{6}-[\dX]"/>
    <xsd:pattern value="\d-\d{3}-\d{5}-[\dX]"/>
    <xsd:pattern value="\d-\d{4}-\d{4}-[\dX]"/>
    <xsd:pattern value="\d-\d{5}-\d{3}-[\dX]"/>
  </xsd:restriction>
</xsd:simpleType>

Un élement isbn déclaré du type ISBN défini ci-dessus pourrait avoir le contenu suivant qui correspond au second motif utilisé ci-dessus.

<isbn>2-311-01400-6</isbn>

Le type suivant Identifier définit un type pour les noms XML. Il aurait aussi pu être décrit avec l'expression rationnelle \i\c*.

<xsd:simpleType name="Identifier">
  <xsd:restriction base="xsd:string">
    <xsd:pattern value="[:_A-Za-z][-.:_0-9A-Za-z]*"/>
  </xsd:restriction>
</xsd:simpleType>

Un élement identifier déclaré du type Identifier défini ci-dessus pourrait avoir le contenu suivant.

<identifier>tns:id-42</identifier>

Pour que le contenu soit valide, il faut que celui-ci, pris dans son intégralité, soit conforme à l'expression rationnelle. Il ne suffit pas qu'un fragment (une sous-chaîne) de celui-ci soit conforme. Le contenu abc123xyz n'est, par exemple, pas conforme à l'expression \d{3} bien que le fragment 123 le soit. Les ancres '^' et '$' sont implicitement ajoutées à l'expression. Pour avoir une expression qui accepte éventuellement un fragment du contenu, il suffit d'ajouter .* au début et à la fin de celle-ci. Le contenu abc123xyz est, par exemple, conforme à l'expression .*\d{3}.*.

5.9.1.4. Liste des facettes

Il faut remarquer que les restrictions par énumération ou par motif se combinent avec un ou logique. Le contenu doit être une des valeurs énumérées ou il doit être conforme à un des motifs. Au contraire, les autres restrictions comme minInclusive et maxInclusive se combinent avec un et logique. Le contenu doit vérifier toutes les contraintes pour être valide.

La liste suivante décrit toutes les facettes. Pour chacune d'entre elles sont donnés les types sur lesquels elle peut s'appliquer.

xsd:enumeration

Cette facette permet d'énumérer explicitement les valeurs autorisées. Elle s'applique à tous les types simples y compris les types construits avec xsd:union et xsd:list.

xsd:pattern

Cette facette permet de donner une expression rationnelle pour contraindre les valeurs. Elle ne s'applique pas uniquement aux types dérivés de xsd:string mais à tous les types simples y compris les types numériques et les types contruits avec xsd:union et xsd:list. L'utilisation avec xsd:decimal permet de restreindre, par exemple, aux nombres ayant 4 chiffres pour la partie entière et 2 pour la partie fractionnaire. Lorsque cette facette est appliquée à un type construit avec xsd:list, la contrainte porte sur les items de la liste et non sur la liste elle-même.

xsd:length, xsd:minLength et xsd:maxLength

Ces trois facettes donnent respectivement une longueur fixe ou des longueurs minimale et maximale. Elles s'appliquent aux types dérivés de xsd:string ainsi qu'aux types construits avec l'opérateur xsd:list.

xsd:minInclusive, xsd:minExclusive, xsd:maxInclusive et xsd:maxExclusive

Ces quatre facettes donnent des valeurs minimale et maximale en incluant ou non la borne donnée. Ces facettes s'appliquent à tous les types numériques ainsi qu'à tous les types de date et d'heure.

xsd:fractionDigits et xsd:totalDigits

Ces deux facettes fixent respectivement le nombre maximal de chiffres de la partie fractionnaire (à droite de la virgule) et le nombre maximal de chiffres en tout. Il s'agit de valeurs maximales. Il n'est pas possible de spécifier des valeurs minimales. De même, il n'est pas possible de spécifier le nombre maximal de chiffres de la partie entière (à gauche de la virgule). Ces deux facettes s'appliquent uniquement aux types numériques dérivés de xsd:decimal. Ceci inclut tous les types entiers mais exclut les types xsd:float et xsd:double.

xsd:whiteSpace

Cette facette est particulière. Elle ne restreint pas les valeurs valides mais elle modifie le traitement des caractères d'espacement à l'analyse lexicale. Cette facette peut prendre les trois valeurs preserve, replace et collapse qui correspondent à trois modes de fonctionnement de l'analyseur lexical.

preserve

Dans ce mode, les caractères d'espacement sont laissés inchangés par l'analyseur lexical.

replace

Dans ce mode, chaque caractère d'espacement est remplacé par un espace U+20. Le résultat est donc du type prédéfini xsd:normalizedString.

collapse

Dans ce mode, le traitement du mode précédent replace est d'abord appliqué puis les espaces en début et en fin sont supprimés et les suites d'espaces consécutifs sont remplacées par un seul espace. Le résultat est donc du type prédéfini xsd:token

Cette facette ne s'applique qu'aux types dérivés de xsd:string. Une dérivation ne peut que renforcer le traitement des caractères d'espacement en passant d'un mode à un mode plus strict (preserve replace collapse). Les changements dans l'autre sens sont impossibles.

5.9.2. Types complexes à contenu simple

Les types complexes à contenu simple sont toujours obtenus par extension d'un type simple en lui ajoutant des attributs. La restriction d'un de ces types peut porter sur le type simple du contenu ou/et sur les attributs. Il est possible de remplacer le type du contenu par un type obtenu par restriction. Il est aussi possible de changer le type d'un attribut ou de modifier son utilisation. Un attribut optionnel peut, par exemple, devenir obligatoire. La restriction d'un type complexe à contenu simple donne toujours un type complexe à contenu simple.

Par défaut, le nouveau type complexe défini est identique au type de base. Pour modifier le type du contenu, l'élément xsd:restriction contient un élément xsd:simpleType qui donne explicitement le nouveau type du contenu. Ce type doit être obtenu par restriction du type qui définit le contenu du type de base.

Dans le schéma suivant, un type Base est défini par extension du type simple xsd:string en lui ajoutant un attribut format. Le type Derived est ensuite obtenu en restreignant le type du contenu aux chaînes d'au plus 32 caractères.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Type de base -->
  <xsd:complexType name="Base">
    <xsd:simpleContent>
      <xsd:extension base="xsd:string">
        <xsd:attribute name="format" type="xsd:string"/>
      </xsd:extension>
    </xsd:simpleContent>
  </xsd:complexType>
  <!-- Restriction du type de base -->
  <xsd:complexType name="Derived">
    <xsd:simpleContent>
      <xsd:restriction base="Base">
        <!-- Nouveau type pour le contenu du type Derived -->
        <xsd:simpleType>
          <xsd:restriction base="xsd:string">
            <xsd:maxLength value="32"/>
          </xsd:restriction>
        </xsd:simpleType>
      </xsd:restriction>
    </xsd:simpleContent>
  </xsd:complexType>
  <xsd:element name="base"    type="Base"/>
  <xsd:element name="derived" type="Derived"/>
</xsd:schema>

Dans le fragment de document ci-dessous, les deux éléments base et derived ont des contenus valides pour le schéma précédent.

<base format="string">Une chaîne très longue de plus de 32 caractères</base>
<derived format="string">Une de moins de 32 caractères</derived>

La restriction peut aussi changer les types des attributs et leur utilisation. Les attributs dont certaines propriétés changent sont redéclarés dans le nouveau type. Les autres restent implicitement inchangés. Le type d'un attribut peut être remplacé par un type obtenu par restriction. Ce type peut, bien sûr, être nommé ou anonyme. L'utilisation des attributs peut aussi être restreinte. Un attribut optionnel peut devenir interdit avec use="prohibited" ou obligatoire avec use="required". L'inverse est en revanche interdit. Il est également impossible d'ajouter de nouveaux attributs. Si un attribut possède une valeur par défaut ou une valeur fixe, celle-ci ne peut être ni modifiée ni supprimée.

Dans le schéma suivant, le type de base Base possède plusieurs attributs dont le type dérivé Derived modifie l'utilisation.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Type de base -->
  <xsd:complexType name="Base">
    <xsd:simpleContent>
      <xsd:extension base="xsd:string">
        <xsd:attribute name="decimal"  type="xsd:decimal"/>
        <xsd:attribute name="string"   type="xsd:string"/>
        <xsd:attribute name="optional" type="xsd:string"/>
        <xsd:attribute name="required" type="xsd:string" use="required"/>
        <xsd:attribute name="fixed"    type="xsd:string" fixed="Fixed"/>
      </xsd:extension>
    </xsd:simpleContent>
  </xsd:complexType>
  <!-- Restriction du type de base -->
  <xsd:complexType name="Derived">
    <xsd:simpleContent>
      <xsd:restriction base="Base">
        <!-- Restriction du type de l'attribut -->
        <xsd:attribute name="decimal" type="xsd:integer"/>
        <!-- Le nouveau type doit être dérivé du type initial -->
        <xsd:attribute name="decimal" type="xsd:string"/>
        <!-- Restriction du type de l'attribut avec un type anonyme -->
        <xsd:attribute name="string">
          <xsd:simpleType>
            <xsd:restriction base="xsd:string">
              <xsd:maxLength value="32"/>
            </xsd:restriction>
          </xsd:simpleType>
        </xsd:attribute>
        <!-- Restriction de l'utilisation de l'attribut -->
        <xsd:attribute name="optional" type="xsd:string" use="required"/>
        <!-- Impossible d'étendre l'utilisation de l'attribut -->
        <xsd:attribute name="required" type="xsd:string"/>
        <!-- Impossible de changer ou supprimer la valeur fixe -->
        <xsd:attribute name="fixed"    type="xsd:string"/>
        <!-- Impossible d'ajouter un nouvel attribut -->
        <xsd:attribute name="newattr"  type="xsd:string"/>
      </xsd:restriction>
    </xsd:simpleContent>
  </xsd:complexType>
  ...
</xsd:schema>

Il est encore possible de changer simultanément le type du contenu et certaines propriétés des attributs. Dans le schéma suivant, le type est restreint au chaînes d'au plus 32 caractères et le type xsd:decimal de l'attribut decimal est remplacé par le type xsd:integer qui est bien un type dérivé de xsd:decimal.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Type de base -->
  <xsd:complexType name="Base">
    <xsd:simpleContent>
      <xsd:extension base="xsd:string">
        <xsd:attribute name="decimal"   type="xsd:decimal"/>
        <xsd:attribute name="unchanged" type="xsd:string"/>
      </xsd:extension>
    </xsd:simpleContent>
  </xsd:complexType>
  <!-- Restriction du type de base -->
  <xsd:complexType name="Derived">
    <xsd:simpleContent>
      <xsd:restriction base="Base">
        <xsd:simpleType>
          <!-- Nouveau type pour le contenu du type Derived -->
          <xsd:restriction base="xsd:string">
            <xsd:maxLength value="32"/>
          </xsd:restriction>
        </xsd:simpleType>
        <!-- Restriction du type de l'attribut -->
        <xsd:attribute name="decimal"  type="xsd:integer"/>
        <!-- Attribut unchanged inchangé -->
      </xsd:restriction>
    </xsd:simpleContent>
  </xsd:complexType>
  <xsd:element name="base"    type="Base"/>
  <xsd:element name="derived" type="Derived"/>
</xsd:schema>

Dans le fragment de document ci-dessous, les deux éléments base et derived ont des contenus valides pour le schéma précédent.

<base decimal="3.14">Une chaîne très longue de plus de 32 caractères</base>
<derived decimal="3">Une de moins de 32 caractères</derived>

5.9.3. Types complexes à contenu complexe

La restriction d'un type complexe permet d'imposer des contraintes aussi bien au contenu qu'aux attributs. La restriction doit rester fidèle au principe que tous les contenus possibles du type restreint doivent être valides pour le type de base. Il est, par exemple, possible de changer le type d'un élément en un type restreint ou de changer le nombre d'occurrences d'un éléments ou d'un bloc avec les attributs minOccurs et maxOccurs. Les restrictions portant sur les attributs sont identiques à celles possibles pour un type complexe à contenu simple.

Le nouveau type est défini en écrivant sa définition comme s'il s'agissait d'une première définition. Dans le schéma suivant, le type Shortname est obtenu par restriction du type Name. La valeur de l'attribut maxOccurs pour l'élément firstname passe de unbounded à 1. L'attribut id devient obligatoire.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="names">
    <xsd:complexType>
      <xsd:choice maxOccurs="unbounded">
        <xsd:element name="name"      type="Name"/>
        <xsd:element name="shortname" type="Shortname"/>
      </xsd:choice>
    </xsd:complexType>
  </xsd:element>
  <!-- Type de base -->
  <xsd:complexType name="Name">
    <xsd:sequence>
      <!-- Nombre illimité d'occurrences de l'élément firstname -->
      <xsd:element name="firstname" type="xsd:string" maxOccurs="unbounded"/>
      <xsd:element name="lastname"  type="xsd:string"/>
    </xsd:sequence>
    <xsd:attribute name="id" type="xsd:ID"/>
  </xsd:complexType>
  <!-- Restriction du type Name -->
  <xsd:complexType name="Shortname">
    <xsd:complexContent>
      <xsd:restriction base="Name">
        <xsd:sequence>
          <!-- Nombre limité d'occurrences de l'élément firstname -->
          <xsd:element name="firstname" type="xsd:string" maxOccurs="1"/>
          <xsd:element name="lastname"  type="xsd:string"/>
        </xsd:sequence>
        <!-- Attribut id obligatoire -->
        <xsd:attribute name="id" type="xsd:ID" use="required"/>
      </xsd:restriction>
    </xsd:complexContent>
  </xsd:complexType>
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1"?>
<names xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <name>
    <firstname>Elizabeth II</firstname>
    <firstname>Alexandra</firstname>
    <firstname>Mary</firstname>
    <lastname>Windsor</lastname>
  </name>
  <shortname id="id-42">
    <firstname>Bessiewallis</firstname>
    <lastname>Warfield</lastname>
  </shortname>
</names>

Il est aussi possible de restreindre un type complexe en remplaçant le type d'un élément par un type dérivé. Dans l'exemple suivant, le type de l'élément integer est xsd:integer dans le type Base. Ce type est remplacé par le type xsd:nonNegativeInteger dans le type Restriction. Le schéma suivant déclare deux éléments locaux de même nom integer mais de types différents.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:complexType name="Base">
    <xsd:sequence>
      <xsd:element name="integer" type="xsd:integer"/> 
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="Restriction">
    <xsd:complexContent>
      <xsd:restriction base="Base">
        <xsd:sequence>
          <xsd:element name="integer" type="xsd:nonNegativeInteger"/>
        </xsd:sequence>
      </xsd:restriction>
    </xsd:complexContent>
  </xsd:complexType>
  ...
</xsd:schema>

Une restriction d'un type complexe à contenu complexe peut aussi supprimer un des choix possibles dans un élément xsd:choice. Dans l'exemple suivant, le choix integer a été supprimé dans le type Float.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Type de base -->
  <xsd:complexType name="Number">
    <xsd:choice>
      <xsd:element name="integer" type="xsd:integer"/> 
      <xsd:element name="float"   type="xsd:float"/> 
      <xsd:element name="double"  type="xsd:double"/> 
    </xsd:choice>
  </xsd:complexType>
  <!-- Restriction du type de base -->
  <xsd:complexType name="Float">
    <xsd:complexContent>
      <xsd:restriction base="Number">
        <xsd:choice>
          <!-- Suppression de l'élément integer -->
          <xsd:element name="float"   type="xsd:float"/> 
          <xsd:element name="double"  type="xsd:double"/> 
        </xsd:choice>
      </xsd:restriction>
    </xsd:complexContent>
  </xsd:complexType>
  ...
</xsd:schema>

Le document suivant est valide pour le schéma précédent. Il utilise une substitution de type avec l'attribut xsi:type pour changer le type de l'élément number en Float.

<?xml version="1.0" encoding="iso-8859-1"?>
<numbers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <number>
    <integer>42</integer>
  </number>
  <number xsi:type="Float">
    <!-- Élément integer impossible -->
    <integer>42</integer>
    <float>3.14</float>
  </number>
</numbers>

5.10. Substitutions

Les schémas XML prévoient plusieurs mécanismes de substitution au niveau des types et des éléments. Dans la substitution de type, un document peut changer explicitement le type associé à un élément afin d'y placer un contenu différent de celui prévu par le schéma. La substitution d'éléments va encore plus loin. Un document peut remplacer un élément par un autre élément.

Les substitutions ne sont pas toujours possibles. Une première condition pour qu'elles puissent s'effectuer est que les types soient compatibles. Il faut que le type de substitution soit un type dérivé du type initial. Les substitutions sont donc étroitement liées aux différentes façons d'obtenir des types dérivés par extension ou restriction.

Une seconde condition pour rendre possible les substitutions est que celles-ci doivent être autorisées par le schéma. Les schémas possèdent différents outils pour contrôler les substitutions.

5.10.1. Annihilation

L'annihilation est un mécanisme qui permet de mettre aucun contenu à un élément alors que le type de l'élément prévoit que le contenu est normalement non vide. Cette notion correspond à l'absence de valeur telle qu'elle existe dans les bases de données.

Le schéma doit d'abord autoriser le mécanisme en donnant la valeur true à l'attribut nillable de l'élément xsd:element. La valeur par défaut de cet attribut est false. Le document doit ensuite, explicitement, déclarer que l'élément n'a pas de valeur en donnant la valeur true à l'attribut xsi:nil qui est dans l'espace de noms des instances de schémas. Le contenu de l'élément doit alors être vide. Dans le schéma suivant, l'élément item est déclaré annihilable.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="list">     
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="item" nillable="true"
                     maxOccurs="unbounded" type="xsd:integer"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Le document suivant est valide pour ce schéma. L'élément item peut avoir un contenu vide si on lui ajoute un attribut xsi:nil avec la valeur true.

<?xml version="1.0" encoding="iso-8859-1"?>
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <!-- Contenu normal -->
  <item>123</item>
  <!-- Contenu vide non autorisé -->
  <item></item>
  <!-- Contenu annihilé -->
  <item xsi:nil="true"></item>
  <!-- Contenu nécessairement vide : aucun espace -->
  <item xsi:nil="true">   </item>
  <item>789</item>
</list>

5.10.2. Substitution de type

Un type peut remplacer, dans une instance de document, un autre type dont il dérive directement ou non. Soit, par exemple, un élément elem déclaré d'un type BaseType. Si un type ExtentedType a été défini par extension ou restriction du type BaseType, il est possible, dans une instance de document, de mettre un élément elem avec un contenu de type ExtentedType. Pour que le document reste valide, l'élément elem doit avoir un attribut xsi:type qui précise le type de son contenu. Cet attribut est dans l'espace de noms des instances de schémas. La substitution de type est autorisée par défaut. Aucune déclaration n'est nécessaire dans le schéma pour qu'elle puisse avoir lieu dans les documents. La substitution de type peut, en revanche, être forcée ou bloquée par les attributs abstract et block. Dans l'exemple suivant, un type Name est d'abord déclaré puis un type Fullname étend ce type en ajoutant un élément title et un attribut id.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema targetNamespace="http://www.omega-one.org/~carton/"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.omega-one.org/~carton/">
  <xsd:element name="name" type="Name"/>
  ...
  <xsd:complexType name="Name">
    <xsd:sequence>
      <xsd:element name="firstname" type="xsd:string"/>
      <xsd:element name="lastname"  type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="Fullname">
    <xsd:complexContent>
      <xsd:extension base="Name">
        <xsd:sequence>
          <xsd:element name="title" type="xsd:string"/>
        </xsd:sequence>
        <xsd:attribute name="id" type="xsd:ID"/>
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>
</xsd:schema>

Le document suivant est valide pour ce schéma.

<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
<tns:names xmlns:tns="http://www.omega-one.org/~carton/"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <!-- Élément name avec le type tns:Name -->
  <tns:name>
    <firstname>Bessiewallis</firstname>
    <lastname>Warfield</lastname>
  </tns:name>
  <!-- Élément name avec le type tns:Fullname -->
  <tns:name id="id52" xsi:type="tns:Fullname">
    <firstname>Elizabeth II Alexandra Mary</firstname>
    <lastname>Windsor</lastname>
    <title>Queen of England</title>
  </tns:name>
</tns:names>

L'attribut xsi:type peut aussi changer le type d'un élément en un autre type obtenu par restriction du type original. Dans l'exemple suivant, un type Byte est déclaré par restriction du type prédéfini xsd:nonNegativeInteger. Cet type est équivalent au type xsd:byte prédéfini dans les schémas.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema targetNamespace="http://www.omega-one.org/~carton/"
            xmlns="http://www.omega-one.org/~carton/"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="value" type="xsd:integer"/>
  ...
  <xsd:simpleType name="Byte">
    <xsd:restriction base="xsd:nonNegativeInteger">
       <xsd:maxInclusive value="255"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:schema>

Le document suivant est valide pour ce schéma. Il est possible de changer le type de l'élément value en xsd:nonNegativeInteger car ce type prédéfini dérive du type prédéfini xsd:integer. Cet exemple illustre aussi l'utilisation indispensable des espaces de noms. Il est, en effet, nécessaire de déclarer trois espaces de noms : celui des éléments du document, celui des schémas pour le type xsd:nonNegativeInteger et celui des instances de schémas pour l'attribut xsi:type.

<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
<tns:values xmlns:tns="http://www.omega-one.org/~carton/"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <tns:value>-1</tns:value>
  <tns:value xsi:type="xsd:nonNegativeInteger">256</tns:value>
  <tns:value xsi:type="tns:Byte">255</tns:value>
</tns:values>

5.10.3. Groupes de substitution

Il est possible de spécifier qu'un élément peut être remplacé par un autre élément dans les documents instance. Ce mécanisme est différent de l'utilisation de l'attribut xsi:type puisque c'est l'élément même qui est remplacé et pas seulement le type. Le type de l'élément substitué doit avoir un type dérivé du type de l'élément original.

La substitution d'élément se distingue en plusieurs points de la substitution de type. Elle est évidemment beaucoup plus forte car elle affecte les éléments qui peuvent apparaître dans les documents. Pour cette raison, elle doit explicitement être prévue par le schéma par l'intermédiaire de groupes de substitution qui décrivent quel élément peut être remplacé et par quels éléments. En revanche, le document ne signale pas la substitution comme il le fait pour une substitution de type avec l'attribut xsi:type.

Ce mécanisme est mis en œuvre en créant un groupe de substitution. Un groupe est formé d'un élément chef de groupe (group head en anglais) et d'autres éléments qui se rattachent au chef de groupe. Le chef de groupe peut être remplacé dans un document instance par n'importe quel autre élément du groupe. Le chef de groupe n'est pas identifié directement. En revanche, tous les autres éléments déclarent leur rattachement au groupe avec l'attribut substitutionGroup dont la valeur est le nom du chef de groupe. Les groupes de substitution peuvent être bloqués par l'attribut final. Dans l'exemple suivant, le chef de groupe est l'élément integer. Les éléments positive et negative peuvent être substitués à l'élément integer.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Chef de groupe -->
  <xsd:element name="integer"  type="xsd:integer"/>
  <!-- Autres éléments du groupe -->
  <xsd:element name="positive" type="xsd:positiveInteger"
                               substitutionGroup="integer"/>
  <xsd:element name="negative" type="xsd:negativeInteger"
                               substitutionGroup="integer"/>
  <xsd:element name="integers">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="integer" maxOccurs="unbounded"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>    
</xsd:schema>

Le document suivant est valide pour ce schéma. L'élément integers contient des éléments positive et negative remplacent des éléments integer.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<integers>
  <integer>0</integer>
  <!-- Élément positive se substituant à un élément integer -->
  <positive>1</positive>
  <!-- Élément negative se substituant à un élément integer -->
  <negative>-1</negative>
  <integer>-42</integer>
</integers>

Depuis la version 1.1 des schémas, l'attribut substitutionGroup de xsd:element peut contenir plusieurs noms d'éléments. Un élément peut ainsi appartenir à plusieurs groupes de substitution alors que c'était impossible avec la version 1.0.

Les substitutions peuvent être utilisées en cascade. Un élément membre d'un groupe de substitution peut, lui-même, être chef d'un autre groupe de substitution. Les membres de ce dernier groupe peuvent bien sûr remplacer leur chef de groupe mais aussi son chef de groupe. Dans le schéma suivant, l'élément head est le chef d'un groupe comprenant l'élément subs. Cet élément subs est, à son tour, chef d'un groupe de substitution comprenant l'élément subsubs. Cet élément subsubs peut dont remplacer l'élément subs mais aussi l'élément head.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- L'élément chef de groupe -->
  <xsd:element name="head" type="xsd:string"/>
  <!-- Un élément subs pouvant se substituer à head -->
  <xsd:element name="subs" type="xsd:string" substitutionGroup="head"/>
  <!-- Un élément subsubs pouvant se substituer à subs et à head -->
  <xsd:element name="subsubs" type="xsd:string" substitutionGroup="subs"/>
  <xsd:element name="heads">     
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="head" maxOccurs="unbounded"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>    
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<heads>
  <head>Élément head original</head>
  <subs>Substitution de head par subs</subs>
  <subsubs>Substitution de head par subsubs</subsubs>
  <head>Autre élément head original</head>
</heads>

Les définitions circulaires de groupes de substitution sont interdites. Un chef de groupe ne peut pas être membre d'un groupe dont le chef serait lui même membre d'un de ses groupes. Les déclarations suivantes ne sont donc pas valides.

  <xsd:element name="head" type="xsd:string" substitutionGroup="subs"/>
  <xsd:element name="subs" type="xsd:string" substitutionGroup="head"/>

Les groupes de substitution permettent, quelquefois, de compenser les lacunes de l'opérateur xsd:all. Il est, en effet, possible de simuler un opérateur xsd:choice avec un élément abstrait et un groupe de substitution.

Dans le schéma suivant, l'élément number est abstrait. Il doit nécessairement être remplacé, dans un document, par un élément de son groupe de substitution, c'est-à-dire par un élément integer ou par un élément double. Il y a donc le choix entre un élément integer ou un élément double. Le type de l'élément number est xsd:anyType pour que les types xsd:integer et xsd:double des éléments integer et double puissent en dériver.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="properties">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="property" maxOccurs="unbounded"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
  <xsd:element name="property">
    <xsd:complexType>
      <xsd:all>
        <xsd:element name="key" type="xsd:string"/>
        <xsd:element ref="number"/>
        <xsd:element name="condition" type="xsd:string" minOccurs="0"/>
      </xsd:all>
    </xsd:complexType>
  </xsd:element>
  <xsd:element name="number"  type="xsd:anyType" abstract="true"/>
  <xsd:element name="integer" type="xsd:integer" substitutionGroup="number"/>
  <xsd:element name="double"  type="xsd:double"  substitutionGroup="number"/>
</xsd:schema>

5.10.4. Contrôle des substitutions et dérivations

Il existe différents moyens de contrôler les substitutions de types et d'éléments. Les types et éléments abstraits introduits par l'attribut abstract permettent de forcer une substitution en empêchant un type ou un élément d'apparaître dans un document. Les attributs block et final permettent, au contraire, de limiter les substitutions et les définitions de types dérivés.

Les trois attributs abstract, block et final s'appliquent aussi bien aux declarations d'éléments qu'aux définitions de types. Il faut prendre garde au fait que leurs significations dans ces deux cas sont proches mais néanmoins différentes.

Le tableau suivant récapitule les utilisations des trois attributs abstract, block et final pour les types et les éléments.

AttributTypeÉlément
abstractbloque l'utilisation du type dans les documentsbloque la présence de l'élément dans les documents
blockbloque la substitution du type dans les documentsbloque la substitution de type pour cet élément dans les documents
finalbloque la dérivation de types dans le schémabloque l'ajout d'éléments dans le groupe de substitution dans le schéma

5.10.4.1. Facette fixée

Les types simples sont obtenus par restrictions successives des types prédéfinis en utilisant des facettes. Il est possible d'imposer, avec l'attribut fixed, qu'une facette ne puisse plus être modifiée dans une restriction supplémentaire.

L'attribut fixed peut être utilisé dans toutes les facettes xsd:minLength, xsd:maxLength, xsd:minInclusive, …. Sa valeur par défaut est la valeur false. Lorsqu'il prend la valeur true, la valeur de la facette est bloquée et elle ne peut plus être modifiée.

Dans le schéma suivant, le type ShortString est obtenu par restriction du type xsd:string. Il impose une longueur maximale à la chaîne avec la facette xsd:maxLength. Cette facette est fixée avec fixed="true". Le type VeryShortString est obtenu par restriction du type ShortString. Il ne peut pas donner une nouvelle valeur à xsd:maxLength.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Type de base : restriction du type xsd:string -->
  <xsd:simpleType name="ShortString">
    <xsd:restriction base="xsd:string">
      <!-- Facette fixée -->
      <xsd:maxLength value="32" fixed="true"/>
    </xsd:restriction>
  </xsd:simpleType>
  <!-- Restriction du type ShortString -->
  <xsd:simpleType name="VeryShortString">
    <xsd:restriction base="ShortString">
      <!-- Facette modifiée -->
      <xsd:minLength value="2"/>
      <!-- Facette impossible à modifier -->
      <xsd:maxLength value="16"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:schema>
...

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
<strings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <string>Une chaîne assez courte</string>
  <string xsi:type="VeryShortString">Très courte</string>
</strings>

5.10.4.2. Type abstrait

Un type complexe peut être déclaré abstrait en donnant la valeur true à l'attribut abstract de l'élément xsd:complexType. Un type simple déclaré avec xsd:simpleType ne peut pas être abstrait.

Ce mécanisme est assez semblable à la notion de classe abstraite des langages de programmation orientés objet comme Java ou C++. Dans ces langages, un type déclaré abstrait peut être utilisé pour dériver d'autres types mais il ne peut pas être instancié. Ceci signifie qu'aucun objet de ce type ne peut être créé. Il est, en revanche, possible de créer des objets des types dérivés.

Lorsqu'un type est déclaré abstrait dans un schéma, celui-ci peut encore être utilisé dans la déclaration d'un élément. En revanche, l'élément ne pourra pas avoir ce type dans un document. Un document valide doit nécessairement opérer une substitution de type par l'intermédiaire de l'attribut xsi:type ou une substitution d'élément par l'intermédiaire d'un groupe de substitution.

Dans l'exemple suivant, on définit un type abstrait Price et un type dérivé InternPrice. L'élément price est du type Price. Il peut être substitué par l'élément internprice qui est de type InternPrice.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Type de base abstrait -->
  <xsd:complexType name="Price" abstract="true">
    <xsd:simpleContent>
      <xsd:extension base="xsd:decimal">
        <xsd:attribute name="currency" type="xsd:string"/>
      </xsd:extension>
    </xsd:simpleContent>
  </xsd:complexType>
  <!-- Type dérivé concret -->
  <xsd:complexType name="InternPrice">
    <xsd:simpleContent>
      <xsd:restriction base="Price">
        <xsd:attribute name="currency" type="xsd:string" use="required"/>
      </xsd:restriction>
    </xsd:simpleContent>
  </xsd:complexType>
  <!-- Élément price de type abstrait -->
  <xsd:element name="price" type="Price"/>
  <!-- Élément interprice de type concret substituable à price -->
  <xsd:element name="internprice" type="InternPrice" substitutionGroup="price"/>
  <xsd:element name="prices">     
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="price" maxOccurs="unbounded"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Le document ci-dessous est valide pour le schéma donné ci-dessus. L'élément price n'apparaît pas avec le type Price qui est abstrait. Soit le type Price est remplacé par le type dérivé InternPrice, soit l'élément price est remplacé par l'élément internprice.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<prices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <!-- Élément de type Price non autorisé -->
  <price>78.9</price>
  <!-- Substitution de type -->
  <price xsi:type="InternPrice" currency="euro">12.34</price>
  <!-- Substitution d'élément -->
  <internprice currency="dollar">45.56</internprice>
</prices>

Dans l'exemple suivant, on définit un type abstrait AbstractType sans contrainte. Ce type est alors équivalent au type xsd:anyType. On dérive ensuite deux types par extension Derived1 et Derived2. Le premier type Derived1 déclare un attribut att de type xsd:string et un élément string comme unique contenu. Le second type Derived2 ne déclare aucun attribut mais il déclare un contenu constitué d'un élément string suivi d'un élément integer.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema targetNamespace="http://www.omega-one.org/~carton/"
            xmlns="http://www.omega-one.org/~carton/"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="value" type="Abstract"/>
  <xsd:element name="values">     
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="value" maxOccurs="unbounded"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>    
  <xsd:complexType name="Abstract" abstract="true"/>
  <xsd:complexType name="Derived1">
    <xsd:complexContent>
      <xsd:extension base="Abstract">
        <xsd:sequence>
          <xsd:element name="string" type="xsd:string"/> 
        </xsd:sequence>
        <xsd:attribute name="att" type="xsd:string"/>
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>
  <xsd:complexType name="Derived2">
    <xsd:complexContent>
      <xsd:extension base="Abstract">
        <xsd:sequence>
          <xsd:element name="string"  type="xsd:string"/> 
          <xsd:element name="integer" type="xsd:integer"/>
        </xsd:sequence>
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>
</xsd:schema>

Le document suivant est valide pour ce schéma. L'élément value apparaît deux fois dans le document mais avec respectivement les types Derived1 et Derived2. Ces types sont déclarés à l'aide de l'attribut xsi:type.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<tns:values xmlns:tns="http://www.omega-one.org/~carton/"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <!-- Élément value de type Abstract impossible -->
  <tns:value xsi:type="tns:Derived1" att="avec un attribut">
    <string>Une chaîne</string>
  </tns:value>
  <tns:value xsi:type="tns:Derived2">
    <string>Un entier</string>
    <integer>-1</integer>
  </tns:value>
</tns:values>

5.10.4.3. Élément abstrait

Un élément peut être déclaré abstrait en donnant la valeur true à l'attribut abstract de l'élément xsd:element. Un élément déclaré abstrait peut être utilisé dans la construction d'un type pour un autre élément. En revanche, il ne peut pas apparaître dans un document instance. L'élément doit nécessairement être remplacé par un autre élément. Cette substitution est uniquement possible lorsque l'élément abstrait est le chef d'un groupe de substitution. Il peut alors être remplacé par n'importe quel membre du groupe.

La contrainte imposée en rendant un élément abstrait est plus forte que celle imposée en rendant un type abstrait. Il n'est, en effet, plus possible de remplacer le type. Il faut nécessairement remplacer l'élément.

Dans le schéma suivant, l'élément value est déclaré abstrait. Il est le chef d'un groupe qui comprend uniquement l'élément other qui peut donc le remplacer.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Un élément value abstrait -->
  <xsd:element name="value" type="xsd:string" abstract="true"/>
  <!-- Un élément other pouvant se substituer à value -->
  <xsd:element name="other" type="String27" substitutionGroup="value"/>
  <!-- Type obtenu par restriction de xsd:string -->
  <xsd:simpleType name="String27">
    <xsd:restriction base="xsd:string">
      <xsd:length value="27"/>
    </xsd:restriction>
  </xsd:simpleType>
  ...

Le document suivant est valide pour le schéma précédent. L'élément abstrait value n'apparaît pas. Il est systématiquement remplacé par l'élément other.

<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
<values xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <!-- Élément value impossible -->
  <value>Une chaîne d'une autre longueur</value>
  <!-- Élément value impossible même avec une substitution de type -->
  <value xsi:type="String27">Une chaîne de 27 caractères</value>
  <!-- Substitution d'élément -->
  <other>Une chaîne de même longueur</other>
</values>

5.10.4.4. Type bloqué

Il est possible, dans un schéma de limiter dans les documents les substitutions de types. L'attribut block de l'élément xsd:complexType permet d'empêcher qu'un élément du type défini puisse prendre un autre type dérivé dans un document instance. La valeur de cet attribut est soit la chaîne #all soit une liste de valeurs parmi les valeurs extension et restriction. Les valeurs énumérées ou toutes pour #all bloquent les différents types qui peuvent remplacer le type pour un élément. La valeur par défaut de cet attribut est donnée par la valeur de l'attribut blockDefault de l'élément xsd:schema.

Lorsque restriction apparaît, par exemple, dans la valeur de l'attribut block de la définition d'un type complexe, celui-ci ne peut pas être remplacé dans un document par un type obtenu par restriction. Cette contrainte s'applique aux substitutions de types et d'éléments. Il n'est pas possible de changer le type d'un élément avec l'attribut xsi:type. Il n'est pas possible non plus de substituer l'élément par un autre élément dont le type est obtenu par restriction.

Dans le schéma suivant, les types Extension et Restriction sont respectivement obtenus par extension et restriction du type Base. La définition de ce type Base contient block="#all". Ceci impose que l'élément value ne peut pas changer son type en le type Restriction ou Restriction avec l'attribut xsi:type. L'élément subs ne peut pas se substituer à l'élément value car son type est Extension. En revanche, l'élément sametype peut se substituer à l'élément value car son type est Base.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="value" type="Base"/>
  <!-- Élément du même type dans le groupe de substitution -->
  <xsd:element name="sametype" type="Base"   substitutionGroup="value"/>
  <!-- Élément d'un type dérivé dans le groupe de substitution -->
  <xsd:element name="subst" type="Extension" substitutionGroup="value"/>
  ...
  <!-- Type de base ne pouvant pas être substitué dans les documents -->
  <xsd:complexType name="Base" block="#all">
    <xsd:sequence>
      <xsd:element name="integer" type="xsd:integer"/> 
    </xsd:sequence>
  </xsd:complexType>
  <!-- Type obtenu par extension du type de base -->
  <xsd:complexType name="Extension">
    <xsd:complexContent>
      <xsd:extension base="Base">
        <xsd:attribute name="att" type="xsd:string"/>
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>
  <!-- Type obtenu par restriction du type de base -->
  <xsd:complexType name="Restriction">
    <xsd:complexContent>
      <xsd:restriction base="Base">
        <xsd:sequence>
          <xsd:element name="integer" type="xsd:nonNegativeInteger"/>
        </xsd:sequence>
      </xsd:restriction>
    </xsd:complexContent>
  </xsd:complexType>
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<values xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <value>
    <integer>-1</integer>
  </value>
  <!-- Substitution autorisée avec le même type --> 
  <sametype>
    <integer>-1</integer>
  </sametype>
  <!-- Élément substitué d'un type dérivé impossible -->
  <subst att="Un attribut">
    <integer>-1</integer>
  </subst>
  <!-- Élément value de type Extension impossible -->
  <value xsi:type="Extension" att="Un attribut">
    <integer>1</integer>
  </value>
  <!-- Élément value de type Restriction impossible -->
  <value xsi:type="Restriction">
    <integer>1</integer>
  </value>
</values>

Un type dérivé peut être contruit à partir d'un type de base en utilisant plusieurs dérivations successives. Ceci peut être, par exemple, une extension, ensuite une restriction puis finalement une nouvelle extension. La substitution du type de base par le type dérivé est bloquée si l'attribut block, dans la définition du type de base, mentionne une dérivation utilisée par le type dérivé. Ceci est vrai que la dérivation bloquée soit la première ou non. Dans l'exemple précédent, si l'attribut block contient restriction, le type dérivé ne peut pas remplacer le type de base car la seconde dérivation est une restriction.

Dans le schéma suivant, le type List bloque sa substitution par un type obtenu par extension. Le type ShortList est obtenu par restriction du type List et le type AttrShortList est obtenu par extension du type ShortList. Le type ShortList peut se substituer au type List. Au contraire, le type AttrShortList ne peut pas se substituer au type List car il y a une dérivation par extension entre le type List et le type AttrShortList.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Type de base ne pouvant pas être substitué par une extension -->
  <xsd:complexType name="List" block="extension">
    <xsd:sequence>
      <xsd:element name="item" type="xsd:string" maxOccurs="unbounded"/>
    </xsd:sequence>
  </xsd:complexType>
  <!-- Restriction du type de base -->
  <xsd:complexType name="ShortList">
    <xsd:complexContent>
      <xsd:restriction base="List">
        <xsd:sequence>
          <!-- Nombre limité d'éléments item -->
          <xsd:element name="item" type="xsd:string" maxOccurs="8"/>
        </xsd:sequence>
      </xsd:restriction>
    </xsd:complexContent>
  </xsd:complexType>
  <!-- Extension de la restriction du type de base -->
  <xsd:complexType name="AttrShortList">
    <xsd:complexContent>
      <xsd:extension base="ShortList">
        <!-- Ajout d'un attribut -->
        <xsd:attribute name="length" type="xsd:integer"/>
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>
  <xsd:element name="list" type="List"/>
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<!-- Type ShortList possible mais type AttrShortList impossible -->
<list xsi:type="ShortList" 
      xsi:type="AttrShortList" length="3"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <item>Premier item</item>
  <item>Deuxième item</item>
  <item>Troisième item</item>
</list>

5.10.4.5. Élément bloqué

L'attribut block peut aussi apparaître dans la déclaration d'un élément. L'attribut block de l'élément xsd:element permet d'empêcher que cet élément puisse prendre un autre type dérivé dans un document instance. La valeur de cet attribut est soit la chaîne #all soit une liste de valeurs parmi les valeurs extension, restriction et substitution. Les valeurs énumérées ou toutes pour #all bloquent les différents types qui peuvent remplacer le type pour un élément. La valeur par défaut de cet attribut est donnée par la valeur de l'attribut blockDefault de l'élément xsd:schema.

Dans le schéma suivant, l'élément integer bloque toutes les substitutions de types dans les documents avec block="restriction extension". Ce blocage empêche de changer le type en un type dérivé avec l'attribut xsi:type. Il empêche également l'élément positive de se substituer à l'élément integer car son type est obtenu par restriction du type xsd:integer. En rechanche, l'élément sametype dont le type est aussi xsd:integer peut rempalcer l'élément integer.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="integer"  type="xsd:integer" 
                               block="restriction extension"/>
  <!-- Élément avec le même type -->
  <xsd:element name="sametype" type="xsd:integer" 
                               substitutionGroup="integer"/>
  <!-- Élément avec un type obtenu par restriction -->
  <xsd:element name="positive" type="xsd:positiveInteger" 
                               substitutionGroup="integer"/>
  ...
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<integers xmlns:xsd="http://www.w3.org/2001/XMLSchema"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <integer>0</integer>
  <!-- Substitution par un élément de même type -->
  <sametype>1</sametype>
  <!-- Substitution de type impossible -->
  <integer xsi:type="xsd:positiveInteger">1</integer>
  <!-- Substitution d'élément avec un type dérivé impossible -->
  <positive>1</positive>
</integers>

L'attribut block de l'élément xsd:element peut aussi contenir la valeur substitution. Cette valeur a un effet très proche de l'attribut final avec la valeur #all. Elle empêche les éléments du groupe de substitution de remplacer leur chef de groupe dans les documents instance. Cela anihile l'intérêt de créer un groupe de substitution puisque ses membres ne peuvent pas réellement se substituer à leur chef de groupe.

Dans le schéma suivant, les éléments sametype et positive appartiennent au groupe de substitution de l'élément integer. En rechanche, ils ne peuvent pas se substituer à cet élément en raison de la valeur substitution de l'attribut block. La substitution de type avec xsi:type reste toujours possible.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="integer"  type="xsd:integer" block="substitution"/>
  <!-- Élément avec le même type -->
  <xsd:element name="sametype" type="xsd:integer" 
                               substitutionGroup="integer"/>
  <!-- Élément avec un type obtenu par restriction -->
  <xsd:element name="positive" type="xsd:positiveInteger" 
                               substitutionGroup="integer"/>
  ...
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<integers xmlns:xsd="http://www.w3.org/2001/XMLSchema"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <integer>0</integer>
  <!-- Substitution de type -->
  <integer xsi:type="xsd:positiveInteger">1</integer>
  <!-- Substitution d'élément avec le même type impossible -->
  <sametype>1</sametype>
  <!-- Substitution d'élément avec un type dérivé impossible -->
  <positive>1</positive>
</integers>

5.10.4.6. Type final

Il est possible, dans un schéma, de restreindre l'utilisation d'un type pour définir d'autres types. Ce mécanisme s'apparente à la possiblité des langages de programmation orientés objet de bloquer la dérivation d'une classe avec le qualificatif final. Le mécanisme des schémas est plus précis car il permet de bloquer sélectivement les différentes dérivations : restriction, extension, union et liste.

L'attribut final des éléments xsd:simpleType et xsd:complexType permet d'empêcher que le type défini puisse servir de type de base à des constructions ou à des dérivations de types. Pour un type simple, la valeur de cet attribut est soit la chaîne #all soit une liste de valeurs parmi les valeurs restriction, list et union. Il est donc impossible de bloquer les extensions d'un type simple. Pour un type complexe, la valeur de cet attribut est soit la chaîne #all soit une liste de valeurs parmi les valeurs extension, restriction. Les valeurs énumérées ou toutes pour #all bloquent les différentes façons de définir des nouveaux types. La valeur par défaut de cet attribut est donnée par la valeur de l'attribut finalDefault de l'élément xsd:schema.

Le schéma suivant n'est pas correct car les définitions des types Extension et Restriction sont impossibles en raison de la valeur #all de l'attribut final dans la définition du type Base. Si la valeur de cet attribut final est changée en restriction, la définition du type Restriction reste incorrecte mais la définition du type Extension devient valide.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  ...
  <!-- L'attribut final="#all" empêche les restrictions et extensions -->
  <xsd:complexType name="Base" final="#all">
    <xsd:sequence>
      <xsd:element name="integer" type="xsd:integer"/> 
    </xsd:sequence>
  </xsd:complexType>
  <!-- Extension du type Base impossible -->
  <xsd:complexType name="Extension">
    <xsd:complexContent>
      <xsd:extension base="Base">
        <xsd:attribute name="att" type="xsd:string"/>
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>
  <!-- Restriction du type Base impossible -->
  <xsd:complexType name="Restriction">
    <xsd:complexContent>
      <xsd:restriction base="Base">
        <xsd:sequence>
          <xsd:element name="integer" type="xsd:nonNegativeInteger"/>
        </xsd:sequence>
      </xsd:restriction>
    </xsd:complexContent>
  </xsd:complexType>
</xsd:schema>

Le bloquage imposé par l'attribut final n'opère que sur la dérivation directe de types. Dans le schéma suivant, le type List bloque les extensions avec final="extension". Le type ShortList est dérivé par restriction du type List. Ce type peut être étendu en un type AttrShortList.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Type de base bloquant les extensions -->
  <xsd:complexType name="List" final="extension">
    <xsd:sequence>
      <xsd:element name="item" type="xsd:string" maxOccurs="unbounded"/>
    </xsd:sequence>
  </xsd:complexType>
  <!-- Restriction du type de base -->
  <xsd:complexType name="ShortList">
    <xsd:complexContent>
      <xsd:restriction base="List">
        <xsd:sequence>
          <!-- Nombre limité d'éléments item -->
          <xsd:element name="item" type="xsd:string" maxOccurs="8"/>
        </xsd:sequence>
      </xsd:restriction>
    </xsd:complexContent>
  </xsd:complexType>
  <!-- Extension de la restriction du type de base -->
  <xsd:complexType name="AttrShortList">
    <xsd:complexContent>
      <xsd:extension base="ShortList">
        <!-- Ajout d'un attribut -->
        <xsd:attribute name="length" type="xsd:integer"/>
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>
  <xsd:element name="list" type="List"/>
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<list length="3" xsi:type="AttrShortList"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <item>Premier item</item>
  <item>Deuxième item</item>
  <item>Troisième item</item>
</list>

La différence entre les attributs final et block est que final concerne la définition de types dérivés alors que block concerne l'utilisation des types dérivés dans les documents instance.

5.10.4.7. Élément final

Il est possible, dans un schéma, de limiter les éléments susceptibles de se substituer à un élément donné. Il est en effet possible d'empêcher sélectivement les éléments d'appartenir à un groupe de substitution en fonction de leur type.

L'attribut final de l'élément xsd:element permet de sélectioner quels éléments peuvent appartenir au groupe de substitution de l'élément. La valeur de cet attribut est soit la chaîne #all soit une liste de valeurs parmi les valeurs restriction et extension. Les valeurs énumérées ou toutes pour #all bloquent les éléments dont le type est obtenu par la dérivation correspondante.

Dans le schéma suivant, l'élément integer empêche les éléments dont le type est dérivé par extension de son type xsd:integer d'appartenir à son groupe de substitution. Comme le type xsd:positiveInteger est obtenu par restriction du type xsd:integer, l'élément positive peut appartenir au groupe de substitution de integer. En revanche, l'élément attributed ne pourrait pas appartenir à ce groupe de substitution car son type Attributed est obtenu par extension du type xsd:integer.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="integer"  type="xsd:integer" final="extension"/>
  <!-- Élément avec un type obtenu par restriction de xsd:integer -->
  <xsd:element name="positive" type="xsd:positiveInteger" 
                               substitutionGroup="integer"/>
  <!-- Élément avec un type obtenu par extension de xsd:integer -->
  <!-- Impossible dans le groupe de substitution de integer -->
  <xsd:element name="attributed" type="Attributed"
                                 substitutionGroup="integer"/>
  <!-- Type obtenu par extension de xsd:integer -->
  <xsd:complexType name="Attributed">
    <xsd:simpleContent>
      <xsd:extension base="xsd:integer">
        <xsd:attribute name="att" type="xsd:string"/>
      </xsd:extension>
    </xsd:simpleContent>
  </xsd:complexType>
  ...
</xsd:schema>

Le document suivant est valide pour le schéma précédent. L'élément attributed ne peut pas remplacer l'élément integer.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<integers>
  <integer>0</integer>
  <!-- Substitution élément -->
  <positive>1</positive>
  <!-- Élément attributed impossible -->
  <attributed att="Un attribut">-1</attributed>
</integers>

5.11. Groupes d'éléments et d'attributs

Il est possible de nommer des groupes d'éléments et des groupes d'attributs afin de pouvoir les réutiliser. Ce mécanisme aide à structurer un schéma complexe et vise à obtenir une meilleure modularité dans l'écriture des schémas. Les groupes d'éléments et d'attributs sont respectivement définis par les éléments xsd:group et xsd:attributeGroup.

Les groupes d'éléments ne doivent pas être confondus avec les groupes de substitution qui permettent de remplacer un élément par un autre.

5.11.1. Groupe d'éléments

L'élément xsd:group permet de définir un groupe d'éléments dont le nom est donné par l'attribut name. L'élément xsd:group doit être enfant de l'élément racine xsd:schema du schéma. Ceci signifie que la portée de la définition du groupe est le schéma tout entier. Le contenu de l'élément xsd:group est un fragment de type nécessairement inclus dans un élément xsd:sequence, xsd:choice ou xsd:all.

Un groupe peut être employé dans la définition d'un type ou la définition d'un autre groupe. L'utilisation d'un groupe est équivalente à l'insertion de son contenu. L'intérêt d'un groupe est de pouvoir l'utiliser à plusieurs reprises et de factoriser ainsi les parties communes à plusieurs types. L'utilisation d'un groupe est introduite par un élément xsd:group avec un attribut ref qui donne le nom du groupe à insérer.

Dans le schéma suivant, le groupe FirstLast est défini puis utilisé dans la définition du groupe Name et du type Person ainsi que dans la définition du type anonyme de l'élément character.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:group name="FirstLast">
    <xsd:sequence>
      <xsd:element name="firstname" type="xsd:string"/>
      <xsd:element name="lastname"  type="xsd:string"/>
    </xsd:sequence>
  </xsd:group>
  <xsd:group name="Name">
    <xsd:choice>
      <xsd:element name="name" type="xsd:string"/>
      <!-- Insertion du contenu du groupe FirstLast -->
      <xsd:group   ref="FirstLast"/>
    </xsd:choice>
  </xsd:group>
  <xsd:complexType name="Person">
    <xsd:sequence>
      <xsd:element name="surname" type="xsd:string"  minOccurs="0"/>
      <!-- Insertion du contenu du groupe Name -->
      <xsd:group   ref="Name"/>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:element name="characters">
    <xsd:complexType>
       <xsd:sequence>
	 <xsd:element name="character" maxOccurs="unbounded">
	   <xsd:complexType>
	     <xsd:sequence>
               <!-- Insertion du contenu du groupe Name -->
	       <xsd:group   ref="Name"/>
	       <xsd:element name="creator" type="Person" 
			    minOccurs="0" maxOccurs="unbounded"/>
	     </xsd:sequence>
	   </xsd:complexType>
	 </xsd:element>
       </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<characters>
  <character>
    <firstname>Gaston</firstname>
    <lastname>Lagaffe</lastname>
    <creator>
      <firstname>André</firstname>
      <lastname>Franquin</lastname>
    </creator>
  </character>
  <character>
    <name>Astérix</name>
    <creator>
      <surname>Al Uderzo</surname>
      <firstname>Albert</firstname>
      <lastname>Uderzo</lastname>
    </creator>
    <creator>
      <firstname>René</firstname>
      <lastname>Goscinny</lastname>
    </creator>
  </character>

Un groupe est en fait un fragment de type qui peut être utilisé à l'intérieur de la définition de n'importe quel type. En revanche, il ne peut pas servir comme type dans la déclaration d'un l'élément. À l'inverse, un type peut servir dans la déclaration d'éléments mais il ne peut pas être directement inclus par un autre type.

Les groupes sont en fait un mécanisme d'abréviation. Ils permettent d'accroître la modularité des schémas en évitant de recopier plusieurs fois le même fragment dans les définitions de différents types.

5.11.2. Groupe d'attributs

Les groupes d'attributs jouent, pour les attributs, un rôle similaire aux groupes d'éléments. Ils permettent de regrouper plusieurs déclarations d'attributs dans le but d'une réutilisation. L'élément xsd:attributeGroup permet de définir un groupe d'attributs dont le nom est donné par l'attribut name. L'élément xsd:attributeGroup doit être enfant de l'élément racine xsd:schema du schéma. Ceci signifie que la portée de la définition du groupe est le schéma tout entier. Le contenu de l'élément xsd:attributeGroup est constitué de déclarations d'attributs introduites par l'élément xsd:attribute.

Dans l'exemple suivant, le groupe d'attributs LangType regroupe les déclaration des deux attributs lang et type.

<xsd:attributeGroup name="LangType">
  <xsd:attribute name="lang" type="xsd:language"/>
  <xsd:attribute name="type" type="xsd:string"/>
</xsd:attributeGroup>

Un groupe d'attributs peut être employé dans la définition d'un type ou la définition d'un autre groupe d'attributs. L'utilisation d'un groupe est équivalente à l'insertion de son contenu. L'utilisation d'un groupe est introduite par un élément xsd:attributeGroup avec un attribut ref qui donne le nom du groupe à insérer.

Le groupe d'attributs LangType peut être employé de la façon suivante dans la définition d'un type complexe nommé ou anonyme. Tout élément du type SomeType défini ci-dessous pourra avoir les attributs lang et type déclarés dans le groupe LangType.

<xsd:complexType name="SomeType">
  <!-- Contenu -->
  ...
  <xsd:attributeGroup ref="LangType"/>
</xsd:complexType>

Il est possible d'utiliser successivement plusieurs groupes d'attributs pour déclarer les attributs d'un type mais il faut une occurrence de xsd:attributeGroup pour chaque groupe utilisé.

<xsd:attributeGroup ref="AttrGroup1"/>
<xsd:attributeGroup ref="AttrGroup2"/>

Un groupe d'attributs peut aussi être utilisé dans la définition d'un autre groupe d'attributs. Le nouveau groupe défini contient tous les attributs du ou des groupes référencés en plus des attributs qu'il déclare explicitement. Ce mécanisme est semblable à l'héritage des classes dans les langages de programmation orientés objet.

<xsd:attributeGroup name="LangTypeClass">
  <xsd:attributeGroup ref="LangType"/>
  <xsd:attribute name="class" type="xsd:string"/>
</xsd:attributeGroup>

Le schéma à l'adresse http://www.w3.org/2001/xml.xsd déclare les quatre attributs particuliers xml:lang, xml:space, xml:base et xml:id. Il définit également un groupe d'attributs xml:specialAttrs permettant de déclarer simultanément ces quatre attributs. Un exemple d'utilisation de celui-ci est donné dans la partie sur l'import de schémas. Cet exemple montre que les noms des groupes d'éléments et des groupes d'attributs sont des noms qualifiés dans l'espace de noms cible du schéma.

5.12. Contraintes de cohérence

Les schémas permettent de spécifier des contraintes globales de cohérence. Celles-ci doivent être vérifiées par un document pour que celui-ci soit valide. Elles ressemblent aux contraintes des DTD portant sur les attributs des types ID, IDREF et IDREFS mais elles sont beaucoup plus générales. Elle peuvent porter sur des éléments ou des attributs. La portée de ces contraintes peut être n'importe quel contenu d'élément et non pas l'intégralité du document comme dans les DTD.

Ces contraintes sont de deux types. Elles peuvent être des contraintes d'unicité comme celle des attributs de type ID des DTD ou des contraintes d'existence comme celle des attributs de type IDREF et IDREFS des DTD. Les contraintes utilisent des expressions XPath mais une connaissance superficielle de ce langage suffit pour les utiliser.

5.12.1. Contraintes d'unicité

Une contrainte d'unicité spécifie que dans le contenu d'un élément donné, il ne peut exister qu'un seul élément ayant une propriété fixée. Cette propriété est très souvent la valeur d'un attribut mais elle peut aussi être formée des valeurs de plusieurs enfants ou attributs. Cette notion est similaire à la notion de clé des bases de données. Elle généralise les attributs de types ID dont la valeur est unique dans tout le document.

Une contrainte d'unicité est donnée par un élément xsd:key ou xsd:unique. Les contraintes introduites par ces deux éléments se présentent de la même façon et ont des sémantiques très proches. L'élément xsd:key ou xsd:unique doit être enfant d'un élément xsd:element qui déclare un élément. Cet élément qui contient la contrainte définit la portée de celle-ci. Les contraintes d'unicité ainsi que les contraintes d'existence doivent être placées après le type de la déclaration.

Chaque élément xsd:key ou xsd:unique possède un attribut name uniquement utilisé par les contraintes d'existence introduites par xsd:keyref et qui peut donc être ignoré pour l'instant. Il contient un élément xsd:selector et des éléments xsd:field possédant chacun un attribut xpath. L'élément xsd:selector détermine sur quels éléments porte la contrainte. La valeur de son attribut xpath est une expression XPath qui sélectionne des éléments concernés. Les éléments xsd:field déterminent quelle est la valeur qui doit être unique. Cette valeur est constituée de plusieurs champs à la manière d'un objet dans les langages de programmation. La valeur de l'attribut xpath de chacun des éléments xsd:field spécifie un champ de la valeur de la clé d'unicité. La contrainte donnée par un élément xsd:key impose que chacun des champs déterminé par les éléments xsd:field soit présent et que la valeur ainsi constituée soit unique pour les éléments sélectionnés par xsd:selector dans le contenu de l'élément définissant la portée. Au contraire, la contrainte donnée par un élément xsd:key n'impose pas que chacun des champs déterminé par les éléments xsd:field soit présent. Elle impose seulement que les éléments ayant tous les champs aient une valeur unique.

Dans l'exemple, la contrainte est décrite au niveau de l'élément bibliography pour exprimer que l'attribut key de book doit être unique dans le contenu de l'élément bibliography.

<!-- Déclaration de l'élément bibliography de type Bibliography -->
<xsd:element name="bibliography" type="Bibliography">
  <!-- Unicité des attributs key des éléments book dans bibliography -->  
  <xsd:key name="dummy">
    <xsd:selector xpath="book"/>
    <xsd:field    xpath="@key"/>
  </xsd:key>
</xsd:element>

Une contrainte décrite avec xsd:key implique que les champs impliqués soient nécessairement présents et non annulables. Une contrainte décrite avec xsd:unique est au contraire seulement vérifiée pour les éléments dont tous les champs spécifiés dans la contrainte sont présents.

5.12.1.1. Portée des contraintes

Le schéma suivant illustre la notion de portée. Il contient deux exemples de contrainte d'unicité. Une première contrainte group.num porte sur les attributs num des éléments group. Cette contrainte est déclarée dans l'élément groups qui est l'élément racine du document ci-dessous. Deux éléments group du document ne peuvent pas avoir la même valeur d'attribut num. La seconde contrainte person.id porte sur les éléments person contenus dans un élément group. Comme cette contrainte est déclarée dans l'élément group, deux éléments person contenus dans le même élément group ne peuvent pas avoir la même valeur d'attribut id. En revanche, deux éléments person contenus dans des éléments group différents peuvent avoir la même valeur d'attribut id.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="groups">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="group" maxOccurs="unbounded"/>
      </xsd:sequence>
    </xsd:complexType>
    <!-- Unicité des attributs num des éléments group -->
    <xsd:unique name="group.num">
      <xsd:selector xpath="group"/>
      <xsd:field    xpath="@num"/>
    </xsd:unique>
  </xsd:element>
  <xsd:element name="group">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="person" maxOccurs="unbounded">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="firstname" type="xsd:string"/>
              <xsd:element name="lastname"  type="xsd:string"/>
            </xsd:sequence>
            <xsd:attribute name="id" type="xsd:string"/>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
      <xsd:attribute name="num" type="xsd:integer"/>
    </xsd:complexType>
    <!-- Unicité des attributs id des éléments person -->
    <xsd:key name="person.id">
      <xsd:selector xpath="person"/>
      <xsd:field    xpath="@id"/>
    </xsd:key>
  </xsd:element>
</xsd:schema>

Le document suivant est valide pour le schéma précédent. Deux éléments person contenus respectivement dans le premier et le deuxième élément group ont la même valeur AC pour l'attribut id.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<groups>
  <group num="1">
    <person id="AC">
      <firstname>Albert</firstname>
      <lastname>Cohen</lastname>
    </person>
    <person id="VH">
      <firstname>Victor</firstname>
      <lastname>Hugo</lastname>
    </person>
  </group>
  <group num="2">
    <person id="AC">
      <firstname>Anders</firstname>
      <lastname>Celsius</lastname>
    </person>
    <person id="SH">
      <firstname>Stephen</firstname>
      <lastname>Hawking</lastname>
    </person>
  </group>
</groups>

5.12.1.2. Valeurs à champs multiples

La valeur qui détermine l'unicité peut être constituée de plusieurs champs. Il suffit pour cela de mettre plusieurs éléments xsd:field dans l'élément xsd:key ou xsd:unique. Deux valeurs sont alors considérées comme différentes si elles diffèrent en au moins un champ.

La contrainte person.names ci-dessous peut remplacer la contrainte person.id du schéma précédent. Elle impose alors que la valeur formée des contenus des deux éléments fitstname et lastname soit différente pour chacun des éléments person. Deux éléments person contenus dans un même élément group peuvent avoir le même contenu textuel pour l'élément firstname ou pour l'élément lastname mais pas pour les deux en même temps.

    <xsd:key name="person.names">
      <xsd:selector xpath="person"/>
      <xsd:field    xpath="firstname"/>
      <xsd:field    xpath="lastname"/>
    </xsd:key>

La contrainte ci-dessus illustre aussi que la valeur peut aussi être donnée par des éléments et pas seulement par des attributs. Le document suivant vérifie la contrainte ci-dessus bien que deux éléments person dans le même élément group aient la même valeur Albert pour l'élément firstname. Deux éléments ayant exactement la même valeur pour l'attribut id sont aussi dans le même élément group mais la contrainte ne porte plus sur cet attribut.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<groups>
  <group num="1">
    <person id="AC">
      <firstname>Albert</firstname>
      <lastname>Cohen</lastname>
    </person>
    <person id="VH">
      <firstname>Victor</firstname>
      <lastname>Hugo</lastname>
    </person>
    <person id="AC">
      <firstname>Anders</firstname>
      <lastname>Celsius</lastname>
    </person>
    <person id="AE">
      <firstname>Albert</firstname>
      <lastname>Einstein</lastname>
    </person>
  </group>
</groups>

Il est bien sûr possible de mettre plusieurs contraintes dans un même élément. Les deux contraintes person.id et person.names pourraient être mises simultanément dans l'élément group comme ci-dessous.

    <xsd:key name="person.id">
      <xsd:selector xpath="person"/>
      <xsd:field    xpath="@id"/>
    </xsd:key>
    <xsd:key name="person.names">
      <xsd:selector xpath="person"/>
      <xsd:field    xpath="firstname"/>
      <xsd:field    xpath="lastname"/>
    </xsd:key>

Avec les contraintes données ci-dessus, le document précédent n'est plus valide car deux éléments person ont la même valeur AC pour leur attribut id.

5.12.1.3. Différence entre xsd:key et xsd:unique

Le schéma précédent illustre également la différence entre les contraintes introduites par les éléments xsd:key et xsd:unique. Une contrainte introduite par xsd:key impose que tous les champs de la valeur soient présents. La contrainte person.id impose donc que l'attribut id soit présent dans chaque élément person même si cet attribut est déclaré optionnel. Au contraire, une contrainte introduite par xsd:unique n'impose pas que tous les champs de la valeurs soient présents. Seuls les éléments ayant tous les champs sont pris en compte dans la vérification de la contrainte. Deux éléments ayant tous les champs ne peuvent avoir tous les champs égaux.

5.12.1.4. Expressions XPath

Les valeurs des attributs xpath des éléments xsd:selector et xsd:field sont des expressions XPath restreintes. L'expression XPath de xsd:selector est relative à l'élément dont la déclaration contient l'élément xsd:unique ou xsd:key. Elle sélectionne uniquement des éléments descendants de cet élément. L'expression XPath de xsd:field est relative aux éléments sélectionnés par xsd:selector. Elle sélectionne uniquement des éléments ou des attributs descendants de ces éléments.

Les seuls opérateurs autorisés dans les expressions XPath des attributs xpath de xsd:selector et xsd:field sont l'opérateur d'union '|' et l'opérateur de composition de chemins '/'. L'opérateur '|' peut apparaître au niveau global mais pas à l'intérieur d'une expression de chemins avec l'opérateur '/'. Les seuls axes autorisés dans ces expressions XPath sont les axes child:: et attribute:: dans leurs syntaxes abrégées ' ' et '@'. L'axe descendant:: peut, en outre, apparaître au début des expressions de chemins dans sa syntaxe abrégée './/'. Les filtres ne sont pas permis dans ces expressions. La contrainte suivante impose, par exemple, que tous les enfants ainsi que tous les enfants de ses enfants group aient des valeurs d'attribut id différentes.

    <xsd:unique name="all.id">
      <xsd:selector xpath="* | group/*"/>
      <xsd:field    xpath="id"/>
    </xsd:unique>

5.12.2. Contraintes d'existence

Une contrainte d'existence spécifie que dans le contenu d'un élément donné, il doit exister un élément ayant une propriété fixée. Comme pour les contraintes d'unicité, cette propriété est très souvent la valeur d'un attribut mais elle peut aussi être formée des valeurs de plusieurs enfants ou attributs. L'idée générale est de référencer un élément par une valeur appelée clé et que cet élément doit exister. Cette idée généralise les attributs de types IDREF et IDREFS des DTD.

Ces contraintes sont introduites par un élément xsd:keyref qui doit être enfant d'un élément xsd:element. Comme pour les contraintes d'unicité, cet élément dans lequel se trouve la contrainte définit la portée de celle-ci.

Chaque élément xsd:keyref possède des attributs name et refer. L'attribut name donne le nom de la contrainte. La valeur de l'attribut refer doit être le nom, c'est-à-dire la valeur de l'attribut name, d'une contrainte d'unicité qui est associée à cette contrainte d'existence. L'élément xsd:keyref contient un élément xsd:selector et des éléments xsd:field possédant chacun un attribut xpath. L'élément xsd:selector sélectionne sur quels éléments porte la contrainte. La valeur de son attribut xpath est une expression XPath qui sélectionne des éléments concernés. Les éléments xsd:field déterminent les différents champs de la valeur servant de clé. La contrainte donnée par un élément xsd:keyref impose que pour chaque élément sélectionné, il existe un élément sélectionné par la contrainte d'unicité associée qui a la même valeur. La contrainte d'unicité reférencée par l'attribut refer doit se trouver dans le même élément que xsd:keyref ou dans un de ses descendants.

Dans l'exemple suivant, la contrainte d'unicité idchapter impose que la valeur d'un attribut id d'un élément chapter soit unique. La contrainte d'existence idref utilise cette contrainte idchapter pour imposer que la valeur d'un attribut idref de tout élément ref soit aussi la valeur d'un attribut id d'un élément chapter. Ceci signifie que tout élément ref référence, par son attribut idref, un chapitre qui existe bien dans le document.

    <!-- Unicité des attributs id des éléments chapter -->
    <xsd:key name="idchapter">
      <xsd:selector xpath="chapter"/>
      <xsd:field    xpath="@id"/>
    </xsd:key>
    <!-- Existence des références idref des éléments ref -->
    <xsd:keyref name="idref" refer="idchapter">
      <xsd:selector xpath=".//ref"/>
      <xsd:field    xpath="@idref"/>
    </xsd:keyref>

Dans l'exemple précédent, la valeur d'un des attributs xpath est l'expression .//ref qui sélectionne tous les descendants de nom ref de l'élément courant. Cette expression est en fait une abréviation de l'expression ./descendant-or-self::node()/ref.

5.12.3. Exemple complet

Voici un exemple de document XML représentant une liste de commandes. Chaque commande concerne un certain nombre d'articles qui sont référencés dans le catalogue donné à la fin.

<?xml version="1.0" encoding="iso-8859-1"?>
<list period="P2D">
  <orders>
    <order date="2008-01-08" time="17:32:28">
      <product serial="101-XX" number="12"/>
      <product serial="102-XY" number="23"/>
      <product serial="101-ZA" number="10"/>
    </order>
    <order date="2008-01-09" time="17:32:28">
      <product serial="101-XX" number="32"/>
    </order>
    <order date="2008-01-09" time="17:32:29">
      <product serial="101-XX" number="32"/>
    </order>
  </orders>
  <catalog>
    <product serial="101-XX">Product n° 1</product>
    <product serial="101-ZA">Product n° 2</product>
    <product serial="102-XY">Product n° 3</product>
    <product serial="102-XA">Product n° 4</product>
  </catalog>
</list>

Le schéma correspondant impose trois contraintes suivantes sur le fichier XML.

  1. Deux commandes order n'ont pas la même date et la même heure.

  2. Deux produits du catalogue n'ont pas le même numéro de série.

  3. Tous les produits référencés dans les commandes sont présents dans le catalogue.

Le début de ce schéma XML est le suivant.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="list">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="orders"  type="Orders"/>
        <xsd:element name="catalog" type="Catalog"/>
      </xsd:sequence>
      <xsd:attribute name="period" type="xsd:duration"/>
    </xsd:complexType>
    <!-- Unicité du couple (date,heure) -->
    <xsd:unique name="dummy">
      <xsd:selector xpath="orders/order"/>
      <xsd:field    xpath="@date"/>
      <xsd:field    xpath="@time"/>
    </xsd:unique>
    <!-- Unicité du numéro de série -->
    <xsd:key name="serial">
      <xsd:selector xpath="catalog/product"/>
      <xsd:field    xpath="@serial"/>
    </xsd:key>
    <!-- Existence dans le catalogue de tout produit commandé -->
    <xsd:keyref name="unused" refer="serial">
      <xsd:selector xpath="orders/order/product"/>
      <xsd:field    xpath="@serial"/>
    </xsd:keyref>
  </xsd:element>

  <!-- Suite du schéma -->
  ...

5.13. Espaces de noms

Un des avantages des schémas par rapport aux DTD est la prise en charge des espaces de noms. L'attribut targetNamespace de l'élément xsd:schema permet de préciser l'espace de noms des éléments et des types définis par le schéma.

5.13.1. Schéma sans espace de noms

Pour une utilisation plus simple, il est possible d'ignorer les espaces de noms. Il est alors possible de valider des documents dont tous les éléments n'ont pas d'espace de noms. Il suffit, pour cela, que les noms des éléments du document ne soient pas qualifiés (sans le caractère ':') et que l'espace de noms par défaut ne soit pas spécifié.

Si l'attribut targetNamespace de l'élément xsd:schema est absent, tous les éléments et types définis dans le schéma sont sans espace de noms. Il faut cependant déclarer l'espace de noms des schémas pour qualifier les éléments des schémas (xsd:element, xsd:complexType, …).

5.13.2. Espace de noms cible

Pour spécifier un espace de noms cible dans lequel sont définis les éléments, l'attribut targetNamespace de l'élément xsd:schema doit contenir l'URI associé à cet espace de noms. La valeur de l'attribut elementFormDefault de l'élément xsd:schema détermine quels éléments sont effectivement définis dans l'espace de noms.

La valeur par défaut de l'attribut elementFormDefault est unqualified. Quand la valeur de elementFormDefault est unqualified, seuls les éléments définis globalement, c'est-à-dire quand l'élément xsd:element est directement enfant de l'élément xsd:schema sont dans l'espace de noms cible. Les autres éléments sont sans espace de noms. Dans le schéma suivant, l'élément global name est dans l'espace de noms identifié par l'URI http://www.omega-one.org/~carton/ alors que les deux éléments locaux firstname et lastname sont sans espace de noms.

<?xml version="1.0" encoding="iso-8859-1"?>
<!-- unqualified est la valeur par défaut de elementFormDefault -->
<xsd:schema targetNamespace="http://www.omega-one.org/~carton/"
            elementFormDefault="unqualified"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="name">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="firstname" type="xsd:string"/>
        <xsd:element name="lastname"  type="xsd:string"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
<tns:name xmlns:tns="http://www.omega-one.org/~carton/">
  <firstname>Gaston</firstname>
  <lastname>Lagaffe</lastname>
</tns:name>

Si la valeur de l'attribut elementFormDefault est qualified, tous les éléments sont dans l'espace de noms cible. Dans le schéma suivant, les trois éléments name, firstname et lastname sont dans l'espace de noms http://www.omega-one.org/~carton/.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema targetNamespace="http://www.omega-one.org/~carton/"
            elementFormDefault="qualified"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="name">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="firstname" type="xsd:string"/>
        <xsd:element name="lastname"  type="xsd:string"/>
      </xsd:sequence>
    </xsd:complexType>
 </xsd:element>
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
<tns:name xmlns:tns="http://www.omega-one.org/~carton/">
  <tns:firstname>Gaston</tns:firstname>
  <tns:lastname>Lagaffe</tns:lastname>
</tns:name>

Le comportement pour les attributs est identique mais il est gouverné par l'attribut attributeFormDefault de l'élément xsd:schema. La valeur par défaut de cet attribut est aussi unqualified.

Les éléments et attributs définis globalement sont toujours dans l'espace de noms cible. Pour les éléments et attributs locaux, il est possible de changer le comportement dicté par elementFormDefault et attributeFormDefault en utilisant l'attribut form des éléments xsd:element et xsd:attribute. Cet attribut peut prendre les valeurs qualified ou unqualified. Le schéma suivant spécifie que l'élément firstname doit être qualifié. Tous les autres éléments locaux comme lastname n'ont pas à être qualifiés car la valeur par défaut de l'attribut elementFormDefault est unqualified.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema targetNamespace="http://www.omega-one.org/~carton/"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="name">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="firstname" type="xsd:string" form="qualified"/>
        <xsd:element name="lastname"  type="xsd:string"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Le document suivant est valide pour le schéma précédent.

<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
<tns:name xmlns:tns="http://www.omega-one.org/~carton/">
  <tns:firstname>Gaston</tns:firstname>
  <lastname>Lagaffe</lastname>
</tns:name>

5.13.3. Noms qualifiés

Lorsqu'un élément, un attribut, un groupe d'éléments, un groupe d'attributs ou encore un type défini globalement est référencé par un attribut ref ou type, la valeur de cet attribut doit contenir le nom qualifié. Ceci oblige à associer un préfixe à l'espace de noms cible et à l'utiliser pour qualifier l'élément ou le type référencé comme dans le schéma suivant.

Les éléments, attributs, groupes et types doivent être nommés avec un nom non qualifié quand ils sont déclarés ou définis. Ils sont à ce moment implicitement qualifiés par l'espace de nom cible. Ceci signifie que les noms apparaissant dans l'attribut name de xsd:element, xsd:attribute, xsd:group, xsd:attributeGroup, xsd:simpleType et xsd:complexType sont toujours des noms locaux, c'est-à-dire sans préfixe.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema targetNamespace="http://www.omega-one.org/~carton/"
            elementFormDefault="qualified"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://www.omega-one.org/~carton/">
  <!-- Référence au type Name par son nom qualifié -->
  <!-- Le nom name de l'élément déclaré n'est pas qualifié -->
  <xsd:element name="name" type="tns:Name" />

  <!-- Le nom Name du type défini n'est pas qualifié -->
  <xsd:complexType name="Name">
    <xsd:sequence>
      <xsd:element name="firstname" type="xsd:string"/>
      <xsd:element name="lastname"  type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

Dans l'exemple précédent, le type Name est référencé par son nom qualifié dans la déclaration de l'élément name. De la même façon, toute référence à un élément déclaré globalement ou à un groupe d'éléments ou d'attributs utilise un nom qualifié. Dans l'exemple suivant, l'élément name apparaît dans la définition d'un autre type Tree qui pourrait être ajoutée au schéma précédent. La définition de ce type est récursive et la référence à lui-même utilise bien sûr le nom qualifié.

<?xml version="1.0" encoding="utf-8"?>
<xsd:schema targetNamespace="http://www.omega-one.org/~carton/"
	    xmlns:tns="http://www.omega-one.org/~carton/"
	    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Déclaration des éléments globaux name et tree -->  
  <xsd:element name="name" type="xsd:string"/>
  <!-- Référence au type Tree par son nom qualifié -->  
  <xsd:element name="tree" type="tns:Tree"/>
  <xsd:complexType name="Tree">
    <xsd:sequence>
      <!-- Référence à l'élément global name par son nom qualifié -->  
      <xsd:element ref="tns:name"/>
      <!-- Référence récursive au type Tree par son nom qualifié -->  
      <!-- Le nom child de l'élément déclaré n'est pas qualifié -->  
      <xsd:element name="child" type="tns:Tree" minOccurs="0" maxOccurs="2"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

L'utilisation de minOccurs avec la valeur 0 pour l'élément child est indispensable pour terminer la récursivité. Sinon, aucun document valide ne peut avoir d'élément de type Tree. Le document suivant est valide pour le schéma précédent. Les éléments globaux tree et name doivent être qualifiés dans le document alors que l'élément local child ne doit pas l'être.

<?xml version="1.0" encoding="iso-8859-1"?>
<tns:tree xmlns:tns="http://www.omega-one.org/~carton/">
  <tns:name>Racine</tns:name>
  <child>
    <tns:name>Fils Gauche</tns:name>
    <child><tns:name>Petit fils</tns:name></child>
  </child>
  <child><tns:name>Fils Droit</tns:name></child>
</tns:tree>

Il est souvent assez lourd de qualifier chacun des noms des objets définis dans le schéma. Une alternative assez commode consiste à rendre l'espace de noms par défaut égal à l'espace de noms cible comme dans l'exemple suivant. Ceci impose bien sûr de ne pas utiliser l'espace de noms par défaut pour les éléments des schémas comme il pourrait être tentant de le faire. Dans la pratique, on associe l'espace de noms par défaut à l'espace de noms cible et on déclare également un préfixe pour cet espace de noms afin de pouvoir y faire référence de façon explicite. Dans l'exemple suivant, l'espace de noms cible identifié par l'URI http://www.omega-one.org/~carton/ est déclaré comme l'espace de noms par défaut et il est également associé au préfixe tns.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema targetNamespace="http://www.omega-one.org/~carton/"
            elementFormDefault="qualified"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.omega-one.org/~carton/"
            xmlns:tns="http://www.omega-one.org/~carton/">
  <!-- Référence au type Name par son nom qualifié -->
  <xsd:element name="name" type="Name" />

  <!-- Définition du type Name -->
  <xsd:complexType name="Name">
    <xsd:sequence>
      <xsd:element name="firstname" type="xsd:string"/>
      <xsd:element name="lastname"  type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

5.14. Imports d'autres schémas

Dans un souci de modularité, il est possible d'importer d'autres schémas dans un schéma à l'aide des éléments xsd:include et xsd:import. L'élément xsd:include est employé lorsque l'espace de noms cible est identique pour le schéma importé. L'élément xsd:import est employé lorsque l'espace de noms cible du schéma importé est différent de celui qui réalise l'import. Les deux éléments xsd:include et xsd:import possèdent un attribut schemaLocation pour donner l'URL du schéma. L'élément xsd:import a, en outre, un attribut namespace pour spécifier l'URI qui identifie l'espace de noms cible du schéma importé.

Le schéma à l'adresse http://www.w3.org/2001/xml.xsd contient les définitions des quatre attributs particuliers xml:lang, xml:space, xml:base et xml:id de l'espace de noms XML. Le schéma suivant importe ce schéma et utilise le groupe d'attributs xml:specialAttrs pour ajouter des attributs à l'élément name.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.omega-one.org/~carton/">
  <xsd:import namespace="http://www.w3.org/XML/1998/namespace"
              schemaLocation="http://www.w3.org/2001/xml.xsd"/>
  <xsd:element name="name">
    <xsd:complexType>    
      <xsd:simpleContent>
        <!-- Le contenu est purement textuel -->
        <xsd:extension base="xsd:string">
          <!-- L'élément name a les attributs xml:lang, xml:space ... -->
          <xsd:attributeGroup ref="xml:specialAttrs"/>
        </xsd:extension>
      </xsd:simpleContent>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Un document valide pour ce schéma est le suivant. L'espace de noms XML est toujours associé au préfixe xml et il n'a pas besoin d'être déclaré.

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
<tns:name xml:lang="fr" xmlns:tns="http://www.omega-one.org/~carton/">
  Élément avec un attribut xml:lang 
</tns:name>

5.15. Expressions rationnelles

Une expression rationnelle désigne un ensemble de chaînes de caractères. Une chaîne donnée est conforme à une expression si elle fait partie des chaînes décrites. Les expressions rationnelles sont construites à partir des caractères et des opérateurs d'union '|', de concaténation et de répétition '?', '*' et '+'.

La syntaxe des expressions rationnelles des schémas est inspirée de celle du langage Perl avec cependant quelques variations. Elle diffère de la syntaxe utilisée par les DTD pour décrire les contenus purs d'éléments car la concaténation n'est plus marquée par la virgule ','. Ces expressions rationnelles sont utilisées par la facette xsd:pattern pour définir des restrictions de types simples. Elles sont également utilisées, en dehors des schémas, par les fonctions XPath matches(), replace() et tokenize() ainsi que l'élément XSLT xsl:analyze-string.

La plupart des caractères se désignent eux-mêmes dans une expression. Ceci signifie que l'expression 'a' (sans les apostrophes) désigne l'ensemble réduit à l'unique chaîne 'a' (sans les apostrophes). Certains caractères, dits spéciaux, ont une signification particulière dans les expressions. La liste des caractères spéciaux comprend les caractères '|', '?', '*', '+', '.', '\', '(', ')', '[', ']', '{', '}'. Pour supprimer le caractère spécial d'un de ces caractères et l'inclure dans une expression, il faut le faire précéder du caractère d'échappement '\'. Quelques caractères d'espacement bénéficient de notations spécifiques. La tabulation U+09, le saut de ligne U+0A et le retour chariot U+0D peuvent être désignés par '\t', '\n' et '\r'. Ces notations ne sont pas indispensables puisque ces trois caractères peuvent être insérés dans un document XML avec la notation &#....

5.15.1. Opérateurs

Les principaux opérateurs des expressions rationnelles sont l'union désignée par le caractère '|', la concaténation notée par simple juxtaposition et les opérateurs de répétition '?', '*', '+'. Comme pour les expressions arithmétiques, on peut utiliser les parenthèses '(' et ')' pour former des groupes. Une expression de la forme expr-1|expr-2 désigne l'union des ensembles de chaînes désignés par expr-1 et expr-2. L'expression a|b désigne, par exemple, l'ensemble contenant les deux chaînes 'a' et 'b' (sans les apostrophes). Une expression de la forme expr-1expr-2 désigne l'ensemble des chaînes obtenues en concaténant (c'est-à-dire en mettant bout à bout) une chaîne conforme à l'expression expr-1 et une chaîne conforme à l'expression expr-2. L'expression ab désigne, par exemple, l'ensemble contenant l'unique chaîne ab. Il est, bien sûr, possible de combiner ces opérateurs. L'expression aba|ba désigne, par exemple, l'ensemble formé des deux chaînes aba et ba. L'expression a(a|b)bc désigne l'ensemble contenant les deux chaînes aabc et abbc.

5.15.2. Répétitions

Les opérateurs '?', '*', '+' permettent de répéter des groupes. Ils utilisent une notation postfixée : ils se placent après leur opérande. S'ils sont placés après un caractère, ils s'appliquent uniquement à celui-ci mais s'ils sont placés après un groupe entre parenthèses, ils s'appliquent à tout le groupe. Ils autorisent, respectivement, la répétition de leur opérande, zéro ou une fois, un nombre quelconque de fois et un nombre positif de fois. Une expression de la forme expr? désigne l'ensemble constitué de la chaîne vide et des chaînes conformes à l'expression expr. Une expression de la forme expr* désigne l'ensemble constitué de toutes les chaînes obtenues en concaténant plusieurs (éventuellement zéro) chaînes conformes à l'expression expr. L'expression a? désigne, par exemple, l'ensemble formé de la chaîne vide et de la chaîne 'a'. Les expressions a* et (a|b)* désignent, respectivement, les ensembles constitués des chaînes formées uniquement de 'a' et les chaînes formées de 'a' et de 'b'.

Les accolades '{' et '}' permettent d'exprimer des répétitions dont le nombre est, soit un entier fixé, soit dans un intervalle donné. Une expression de la forme expr{n}, où n est un entier, désigne l'ensemble constitué de toutes les chaînes obtenues en concaténant exactement n chaînes conformes à l'expression expr. Une expression de la forme expr{m,n}, où m et n sont des entiers, désigne l'ensemble constitué de toutes les chaînes obtenues en concaténant un nombre de chaînes conformes à l'expression expr compris, au sens large, entre m et n. L'entier n peut être omis pour donner une expression de la forme expr{m,}. Le nombre de répétitions doit seulement être supérieur à m. L'expression (a|b){6} désigne, par exemple, les chaînes de longueur 6 formées de 'a' et de 'b'. L'expression (a|b){3,8} désigne les chaînes de longueur comprise entre 3 et 8 formées de 'a' et de 'b'.

5.15.3. Ensembles de caractères

Il est souvent nécessaire de désigner des ensembles de caractères pour construire des expressions rationnelles. Il existe plusieurs façons de décrire ces ensembles.

Le caractère spécial '.' désigne tout caractère autre qu'un retour à la ligne. L'expression a.b désigne, par exemple, l'ensemble de toutes les chaînes de trois caractères dont le premier est 'a', le second n'est pas un retour à la ligne et le dernier est 'b'. L'expression .* désigne l'ensemble de toutes les chaînes ne contenant aucun retour à la ligne.

Un ensemble fini de caractères peut simplement être décrit en donnant ces caractères encadrés par des crochets '[' et ']'. L'expression [aeiouy] désigne l'ensemble des voyelles minuscules et elle est équivalente à l'expression a|e|i|o|u|y. Cette syntaxe est pratique car elle permet d'inclure facilement un intervalle en plaçant un tiret '-' entre les deux caractères qui le délimitent. L'expression [0-9] désigne, par exemple, l'ensemble des chiffres. Il est possible de mettre des caractères et plusieurs intervalles. L'expression [:a-zA-F] désigne l'ensemble des lettres minuscules et majuscules et du caractère ':'. L'ordre des caractères entre les crochets est sans importance. Pour inclure un tiret '-' dans l'ensemble, il faut le placer en premier ou en dernier. Pour inclure un crochet fermant ']', il faut le placer en premier, juste après le crochet ouvrant. L'expression [-az] désigne l'ensemble des trois caractères '-', 'a' et'z'.

Lorsque le premier caractère après le crochet ouvrant est le caractère '^', l'expression désigne le complémentaire de l'ensemble qui aurait été décrit sans le caractère '^'. L'expression [^0-9] désigne, par exemple, l'ensemble des caractères qui ne sont pas un chiffre. Pour inclure un caractère '^' dans l'ensemble, il faut le placer à une autre place que juste après le crochet ouvrant. L'expression [0-9^] désigne, par exemple, l'ensemble formé des chiffres et du caractère '^'.

Un ensemble de caractères peut être décrit comme la différence de deux ensembles entre crochets. La syntaxe prend la forme [...-[...]]... est remplacé par une suite de caractères et d'intervalles comme dans les exemples précédents. L'expression [a-z-[aeiouy]] désigne, par exemple, l'ensemble des consonnes minuscules.

Certains ensembles de caractères fréquemment utilisés peuvent être décrits par le caractère d'échappement '\' suivi d'une lettre. Les différentes combinaisons possibles sont données ci-dessous.

\s

caractères d'espacement, c'est-à-dire l'ensemble [ \t\n\r]

\S

caractères autres que les caractères d'espacement, c'est-à-dire l'ensemble [^ \t\n\r]

\d

chiffres, c'est-à-dire [0-9]

\D

caractères autres que les chiffres, c'est-à-dire [^0-9]

\w

caractères alphanumériques et le tiret '-', c'est-à-dire [-0-9a-zA-Z]

\W

caractères autres que les caractères alphanumériques et le tiret, c'est-à-dire [^-0-9a-zA-Z]

\i

premiers caractères des noms XML, c'est-à-dire [:_a-zA-Z]

\I

caractères autres que les premiers caractères des noms XML, c'est-à-dire [^:_a-zA-Z]

\c

caractères des noms XML, c'est-à-dire [-.:_0-9a-zA-Z]

\C

caractère autres que les caractères des noms XML, c'est-à-dire [^-.:_0-9a-zA-Z]

L'expression \s*\d+\s* désigne, par exemple, une suite non vide de chiffres encadrée, éventuellement, par des caractères d'espacement. L'expression \i\c* désigne l'ensemble des noms XML.

Il est aussi possible de décrire un ensemble de caractères en utilisant une des catégories de caractères Unicode. La syntaxe prend la forme \p{cat} pour l'ensemble des caractères de la catégorie cat et \P{cat} pour son complémentaire. L'expression \p{Lu} désigne, par exemple, toutes les lettres majuscules. Les principales catégories de caractères Unicode sont les suivantes.

CatégorieSignification 
Llettres 
Lulettres majuscules 
Lllettres minuscules 
Nchiffres 
Pcaractère de ponctuation 
ZSéparateurs 

Tableau 5.1. Catégories Unicode