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 du schéma. Les schémas ont été introduits pour combler certaines lacunes des 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 paire avec la hiérarchie des types.
Les DTD ont un 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 espace 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.
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 peut être donné pour la bibliographie. Le cours est essentiellement basé sur des exemples.
<?xml version="1.0" encoding="iso-8859-1"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:annotation>
<xsd:documentation xml:lang="fr"> Schéma XML pour bibliography.xml </xsd:documentation> </xsd:annotation> <xsd:element name="bibliography" type="Bibliography"/>
<xsd:complexType name="Bibliography">
<xsd:sequence> <xsd:element name="book" minOccurs="1" maxOccurs="unbounded">
<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"/>
<xsd:attribute name="lang" type="xsd:NMTOKEN" use="required"/>
</xsd:complexType> </xsd:element> </xsd:sequence> </xsd:complexType> </xsd:schema>
Élément racine | |
Documentation du schéma. | |
Déclaration de l'élément | |
Début de la définition du type
| |
Déclaration de l'élément | |
Déclaration des attributs |
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.
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'URL
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 enfant direct de
l'élément xsd:schema. Sinon, ils sont locaux. Ces
objets et eux seuls peuvent être référencés dans tout le schéma pour être
utilisé. 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
sont nommés. Dans le schéma ci-dessus, le type de l'élément
book est 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.
L'élément racine xsd:schema peut avoir les
attributs suivants.
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
attributeFormDefaultCes 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
finalDefaultCes 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 http://www.liafa.jussieu.fr/~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.liafa.jussieu.fr/~carton" elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.liafa.jussieu.fr/~carton"> ...
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ées par des espaces. Ces URI vont par paires et le
nombre d'URI doit donc être pair. La première URI de chaque paire
identifie un espace de noms et la seconde donne l'adresse du schéma à
utiliser pour les éléments et attributs dans cet espace de noms.
L'espace de noms identifié par la première URI doit donc être l'espace
de noms cible du schéma donné par la seconde. La valeur de l'attribut
schemaLocation prend donc la forme générale
suivante
schemaLocation="namespace1 schema1 namespace2 ... namespaceN schemaN"
où 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'URL
http://www.liafa.jussieu.fr/~carton/.
<?xml version="1.0" encoding="iso-8859-1"?> <bibliography xsi:schemaLocation="http://www.liafa.jussieu.fr/~carton/ bibliography.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> ...
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>
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'autres 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.
La déclaration la plus simple d'un élément prend la forme suivante.
<xsd:element name="element" type="type"/>
où 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"/>
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"/>
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>
Dans la définition d'un élément ou d'un type, il est possible
d'utiliser un élément défini globalement, c'est-à-dire comme fils de
l'élément xsd:schema. Une telle référence s'écrit de
la façon suivante.
<!-- Définition globale de l'élément title --> <xsd:element name="title" type="Title"/> ... <!-- Définition d'un type --> <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.
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>
<xsd:element name="local" type="xsd:string" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="integers">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="local" type="xsd:integer" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Un document valide pour le schéma suivant 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>
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 purement textuels. 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 formés uniquement d'éléments ou des contenus mixtes. 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é 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"/>
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 type de base
mais leur présence comme type de base simplifie le travail.
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:booleanValeur booléenne avec true ou
1 pour vrai et
false ou 0 pour
faux
xsd:byteNombre entier signé sur 8 bits
xsd:unsignedByteNombre entier non signé sur 8 bits
xsd:shortNombre entier signé sur 16 bits
xsd:unsignedShortNombre entier non signé sur 16 bits
xsd:intNombre entier signé sur 32 bits
xsd:unsignedIntNombre entier non signé sur 32 bits
xsd:longNombre entier signé sur 64 bits. Ce type dérive du type
xsd:integer.
xsd:unsignedLongNombre entier non signé sur 64 bits
xsd:integerNombre entier sans limite de précision. Ce type n'est pas
primitif et dérive du type xsd:decimal.
xsd:positiveIntegerNombre entier strictement positif sans limite de précision
xsd:negativeIntegerNombre entier strictement négatif sans limite de précision
xsd:nonPositiveIntegerNombre entier négatif ou nul sans limite de précision
xsd:nonNegativeIntegerNombre entier positif ou nul sans limite de précision
xsd:floatNombre flottant sur 32 bits conforme à la norme IEEE 754
xsd:doubleNombre flottant sur 64 bits conforme à la norme IEEE 754
xsd:decimalNombre décimal sans limite de précision
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:stringChaîne de caractères composée de caractères Unicode
xsd:normalizedStringChaî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:tokenChaî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:Namexsd:QNamexsd:NCNameNom non qualifié,
c'est-à-dire sans caractère ':'
xsd:languageCode 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:anyURIUn URI comme
http://www.liafa.jussieu.fr/~carton/. C'est le
type de l'attribut particulier xml:base.
xsd:base64BinaryDonnées binaires représentées par une chaîne au format Base 64.
xsd:hexBinaryDonnées binaires représentées par une chaîne au format Hex.
Les 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.
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:timeHeure 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:dateDate au format YYYY-MM-DD, par exemple
2008-01-16. Tous les champs sont
obligatoires.
xsd:dateTimeDate et heure au format YYYY-MM-DDThh:mm:ss
comme 2008-01-16T14:07:23. Tous les champs sont
obligatoires.
xsd:durationDurée au format PnYnMnDTnHnMnS comme
P1Y6M, P1M12DT2H et
P1YD3H10S.
xsd:dayTimeDurationDurée au format PnDTnHnMnS comme
P7DT4H3M2S.
xsd:yearMonthDurationDurée au format PnYnM comme
P1Y6M.
xsd:gYearAnnée du calendrier grégorien au format
YYYY comme 2011.
xsd:gYearMonthAnnée et mois du calendrier grégorien au format
YYYY-MM comme 1966-06 pour
juin 1966.
xsd:gMonthMois du calendrier grégorien au format MM
comme 01 pour janvier.
xsd:gMonthDayJour et mois du calendrier grégorien au format
MM-DD comme 12-25 pour le jour
de Noël.
xsd:gDayJour (dans le mois) du calendrier grégorien au format
DD comme 01 pour le premier
de chaque mois.
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:IDnom XML identifiant un élément
xsd:IDREFréférence à un élément par son identifiant
xsd:IDREFSliste de références à des éléments par leurs identifiants
xsd:NMTOKENxsd:NMTOKENSliste de jetons separés par des espaces
xsd:ENTITYentité externe non XML
xsd:ENTITIESliste d'entités externes non XML separés par des espaces
xsd:NOTATIONnotation
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>
Les types complexes définissent des contenus purs (constitués
uniquement d'éléments), des contenus textuels ou des contenus mixes.
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>
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)* >
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.
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.
<xsd:element name="link" type="Link"/> <xsd:complexType name="Link"> <xsd:attribute name="ref" type="xsd:IDREF" use="required"/> </xsd:complexType>
Un fragement de document valide peut être le suivant.
<link ref="id-42"/>
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)>
Un 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.
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 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>
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>
L'opérateur xsd:union définit un nouveau type
simple dont les valeurs sont celles des types listés dans l'attribut
memberTypes.
Voici à titre d'exemple, le type de l'attribut maxOccurs tel qu'il pourrait être défini dans un
schéma pour les schémas.
<xsd:attribute name="maxOccurs" type="IntegerOrUnbounded"/>
<xsd:simpleType name="IntegerOrUnbounded">
<xsd:union memberTypes="Unbounded xsd:nonNegativeInteger"/>
</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>
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 pertuberaient 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>
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>
<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>
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 espace 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 à 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"> <li>Romancier</li> <li>Poète</li> <li>Dramaturge</li> </html:ul> </person>
Il existe également un élément xsd:anyAttribute
pour autoriser des attributs externes au schéma.
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>
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 dans
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>
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>
Un attribut peut aussi être global lorsque sa déclaration 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 multiple. Elle accroît la modularité 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.liafa.jussieu.fr/~carton/"> <text>Texte en français</text> <text tns:lang="en">Text in english</text> </tns:texts>
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>
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"/>
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.liafa.jussieu.fr/~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.liafa.jussieu.fr/~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.liafa.jussieu.fr/~carton/">
<firstname>Victor</firstname>
<lastname>Hugo</lastname>
</person>
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 objets 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.
L'extension d'un 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"/>
Un fragment de document valide peut être le suivant.
<price currency="euro">3.14</price>
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>
Un fragment de document valide peut être le suivant.
<price currency="euro" country="France">3.14</price>
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 semblabe 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>
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.
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.
Il est possible de restreindre les contenus en donnant une valeur
minimale et/ou une valeur minimale avec les éléments
xsd:minInclusive,
xsd:minExclusive,
xsd:maxInclusive et
xsd:maxExclusive. Ces contraintes ne s'appliquent
qu'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>
La restriction par intervale 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 1er janvier 2001 exclus -->
<xsd:minExclusive value="2001-01-01"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
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>
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.
<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>
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>
Pour que la contenu soit valide, il faut que le contenu, 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}.*.
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 décrit par 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:enumerationCette 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:patternCette 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:maxLengthCes trois facettes donnent respectivement une longueur fixe ou
des longueurs minimale et maximale. Elle 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:maxExclusiveCes 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:totalDigitsCes 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:whiteSpaceCette 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.
preserveDans ce mode, les caractères d'espacement sont laissés inchangés par l'analyseur lexical.
replaceDans ce mode, chaque caractère d'espacement est remplacé
par un espace #x20. Le résultat est donc du
type prédéfini xsd:normalizedString.
collapseDans 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.
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:schema>
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 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>
<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 de
l'attribut decimal est changé en le type
xsd:integer.
<?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>
<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:schema>
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 les contenus du type restreint doivent être
valide 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" standalone="no" ?>
<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.
<?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" standalone="yes"?>
<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>
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.
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 peut apparaître 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 annihilé --> <item xsi:nil="true"></item> <!-- Contenu nécessairement vide --> <item xsi:nil="true"> </item> <item>789</item> </list>
Un type peut remplacer dans une instance de document un 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 nom des instances de schémas. 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.liafa.jussieu.fr/~carton/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.liafa.jussieu.fr/~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.liafa.jussieu.fr/~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.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema targetNamespace="http://www.liafa.jussieu.fr/~carton/"
xmlns="http://www.liafa.jussieu.fr/~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.liafa.jussieu.fr/~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>
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. 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 qui sont
substitués à des éléments integer.
<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?> <integers> <integer>0</integer> <positive>1</positive> <negative>-1</negative> </integers>
Un élément donné est nécessairement le chef d'un unique groupe
puisque chaque groupe est identifié par son chef. De même, un même
élément ne peut appartenir qu'à au plus un groupe puisque l'attribut
substitutionGroup de xsd:element ne
peut contenir qu'un seul nom d'élément. La version 1.1 des schémas
autorise l'attribut substitutionGroup à contenir
plusieurs noms d'éléments. Un élément peut ainsi appartenir à plusieurs
groupes de substitution.
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> </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>
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.
| Attribut | Type | Élément |
abstract | bloque l'utilisation du type dans les documents | bloque la présence de l'élément dans les documents |
block | bloque la substitution du type dans les documents | bloque la substitution de type pour cet élément dans les documents |
final | bloque la dérivation de types dans le schéma | bloque l'ajout d'éléments dans le groupe de substitution dans le schéma |
Les types symples 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
ultérieure.
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 lomgueur 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>
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 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.liafa.jussieu.fr/~carton/"
xmlns="http://www.liafa.jussieu.fr/~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.liafa.jussieu.fr/~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>
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>
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>
L'attribut block bloque la substitution par un
type dont une des étapes de dérivation et pas seulement la première
étape est mentionnée dans ses valeurs. Si, par exemple, l'attribut
block d'une définition de type contient
extension, seuls les types obtenus par restrictions
successives de ce type peuvent le remplacer.
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>
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 se substituer dans les documents instance. Cela
anihile l'intéret d'avoir des éléments dans le groupe de substitution
puisque ceux-ci 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.
<?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>
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
correcte.
<?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.
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 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 se substituter à
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>
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.
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 dans son
intégralité. 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"/> <xsd:group ref="FirstLast"/> </xsd:choice> </xsd:group> <xsd:complexType name="Person"> <xsd:sequence> <xsd:element name="surname" type="xsd:string" minOccurs="0"/> <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> <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 la définition de différents types.
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 dans son
intégralité. 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 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. 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.
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.
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:selector 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.
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>
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
ait 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>
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.
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>
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.
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 soir 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.
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.
Deux commandes orders n'ont pas la même date
et la même heure.
Deux produits du catalogue n'ont pas le même numéro de série.
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 -->
...
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.
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 soit 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, …).
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. Les éléments qui sont effectivement définis dans l'espace de noms
dépend de la valeur de l'attribut elementFormDefault de l'élément
xsd:schema.
Si la valeur de l'attribut elementFormDefault
est unqualified qui est sa valeur par
défaut, seuls les éléments définis globalement, c'est-à-dire quand
l'élément xsd:element est directement fils de
l'élément xsd:schema sont dans l'espace de noms
cible. Les autres sont sans espace de noms. Dans le schéma suivant,
l'élément name est dans l'espace de noms
http://www.liafa.jussieu.fr/~carton/ alors que les
éléments 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.liafa.jussieu.fr/~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>
Un document valide pour ce schéma est le suivant.
<?xml version="1.0" encoding="iso-8859-1" standalone="no"?> <tns:name xmlns:tns="http://www.liafa.jussieu.fr/~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.liafa.jussieu.fr/~carton/.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsd:schema targetNamespace="http://www.liafa.jussieu.fr/~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>
Un document valide pour ce schéma est le suivant.
<?xml version="1.0" encoding="iso-8859-1" standalone="no"?> <tns:name xmlns:tns="http://www.liafa.jussieu.fr/~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.liafa.jussieu.fr/~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>
Un document valide pour ce schéma est le suivant.
<?xml version="1.0" encoding="iso-8859-1" standalone="no"?> <tns:name xmlns:tns="http://www.liafa.jussieu.fr/~carton/"> <tns:firstname>Gaston</tns:firstname> <lastname>Lagaffe</lastname> </tns:name>
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.liafa.jussieu.fr/~carton/"
elementFormDefault="qualified"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.liafa.jussieu.fr/~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="iso-8859-1"?> <xsd:schema targetNamespace="http://www.liafa.jussieu.fr/~carton/" xmlns:tns="http://www.liafa.jussieu.fr/~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.
<?xml version="1.0" encoding="iso-8859-1"?>
<tns:tree xmlns:tns="http://www.liafa.jussieu.fr/~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
http://www.liafa.jussieu.fr/~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.liafa.jussieu.fr/~carton/"
elementFormDefault="qualified"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.liafa.jussieu.fr/~carton/"
xmlns:tns="http://www.liafa.jussieu.fr/~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>
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.liafa.jussieu.fr/~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 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.liafa.jussieu.fr/~carton/">
Élément avec un attribut xml:lang
</tns:name>
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énificient 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 &#....
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
désigne l'union des ensembles de chaînes désignés par
expr-1|expr-2expr-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
désigne l'ensemble des chaînes obtenue en concaténant (c'est-à-dire en
mettant bout à bout) une chaîne conforme à l'expression
expr-1expr-2expr-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.
Les opérateurs '?', '*',
'+' permettent de répéter des groupes. Il 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
désigne l'ensemble
constitué de la chaîne vide et des chaînes conformes à l'expression
expr?expr. Une expression de la forme
désigne l'ensemble
constitué de toutes les chaînes obtenues en concaténant plusieurs
(éventuellement zéro) chaînes conformes à l'expression
expr*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
,
où expr{n}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
,
où expr{m,n}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
.
Le nombre de répétitions doit seulement être supérieur à
expr{m,}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'.
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'
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 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 caractère 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 crochet. La syntaxe prend la forme
[...-[...]] où ... 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.
\scaractères
d'espacement, c'est-à-dire l'ensemble
[ \t\n\r]
\Scaractères autres que les caractères d'espacement, c'est-à-dire
l'ensemble [^ \t\n\r]
\dchiffres, c'est-à-dire [0-9]
\Dcaractères autres que les chiffres, c'est-à-dire
[^0-9]
\wcaractères alphanumériques et le tiret '-',
c'est-à-dire [-0-9a-zA-Z]
\Wcaractères autres que les caractères alphanumériques et le tiret,
c'est-à-dire [^-0-9a-zA-Z]
\ipremiers caractères des noms XML, c'est-à-dire
[:_a-zA-Z]
\Icaractères autres que les premiers caractères des
noms XML, c'est-à-dire [^:_a-zA-Z]
\ccaractères des noms XML, c'est-à-dire
[-.:_a-zA-Z]
\Ccaractère autres que les caractères des noms XML,
c'est-à-dire [^-.:_a-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{ pour
l'ensemble des caractères de la catégorie cat}cat
et \P{ pour son
complémentaire. L'expression cat}\p{Lu} désigne, par
exemple, toutes les lettres majuscules. Les principales catégories de
caractères Unicode sont les suivantes.
| Catégorie | Signification | |
|---|---|---|
L | lettres | |
Lu | lettres majuscules | |
Ll | lettres minuscules | |
N | chiffres | |
P | caractère de ponctuation | |
Z | Séparateurs |
Tableau 5.1. Catégories Unicode