• Aucun résultat trouvé

Contraintes de cohérence

5. Schémas XML

5.12. Contraintes de cohérence

<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 [Section 5.14] déclare les quatre attributs particuliers [Section 2.7.4] 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.

5.12. Contraintes de cohérence

Les schémas permettent de spécifier des contraintes globales de cohérence. Celles-ci doivent être vérifiées par un document pour que celui-ci soit valide. Elles ressemblent aux contraintes des DTD portant sur les attributs des

Schémas XML

types ID, IDREF et IDREFS [Section 3.7.2] 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 [Chapitre 6] mais une connaissance superficielle de ce langage suffit pour les utiliser.

5.12.1. Contraintes d'unicité

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

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

Chaque élément xsd:key ou xsd:unique possède un attribut name uniquement utilisé par les contraintes d'existence introduites par xsd:keyref et qui peut donc être ignoré pour l'instant. Il contient un élément xsd:selector et des éléments xsd:field possédant chacun un attribut xpath. L'élément xsd:selector détermine sur quels éléments porte la contrainte. La valeur de son attribut xpath est une expression XPath qui sélectionne des éléments concernés. Les éléments xsd:field déterminent quelle est la valeur qui doit être unique. Cette valeur est constituée de plusieurs champs à la manière d'un objet dans les langages de programmation.

La valeur de l'attribut xpath de chacun des éléments xsd: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 [Section 5.10.1]. Une contrainte décrite avec xsd:unique est au contraire seulement vérifiée pour les éléments dont tous les champs spécifiés dans la contrainte sont présents.

5.12.1.1. Portée des contraintes

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

Schémas XML

revanche, deux éléments person contenus dans des éléments group différents peuvent avoir la même valeur d'attribut id.

<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:element name="person" maxOccurs="unbounded">

<xsd:complexType>

<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>

Schémas XML

</person>

<person id="SH">

<firstname>Stephen</firstname>

<lastname>Hawking</lastname>

</person>

</group>

</groups>

5.12.1.2. Valeurs à champs multiples

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

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

<xsd:key name="person.names">

<xsd:selector xpath="person"/>

<xsd:field xpath="firstname"/>

<xsd:field xpath="lastname"/>

</xsd:key>

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

<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>

<groups>

<group num="1">

<person id="AC">

<firstname>Albert</firstname>

<lastname>Cohen</lastname>

</person>

<person id="VH">

<firstname>Victor</firstname>

<lastname>Hugo</lastname>

</person>

<person id="AC">

<firstname>Anders</firstname>

<lastname>Celsius</lastname>

</person>

<person id="AE">

<firstname>Albert</firstname>

<lastname>Einstein</lastname>

</person>

</group>

</groups>

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

<xsd:key name="person.id">

<xsd:selector xpath="person"/>

Schémas XML

<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>

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

Le schéma précédent illustre également la différence entre les contraintes introduites par les éléments xsd:key et xsd:unique. Une contrainte introduite par xsd:key impose que tous les champs de la valeur soient présents.

La contrainte person.id impose donc que l'attribut id soit présent dans chaque élément person même si cet attribut est déclaré optionnel [Section 5.7.2]. Au contraire, une contrainte introduite par xsd:unique n'impose pas que tous les champs de la valeurs soient présents. Seuls les éléments ayant tous les champs sont pris en compte dans la vérification de la contrainte. Deux éléments ayant tous les champs ne peuvent avoir tous les champs égaux.

5.12.1.4. Expressions XPath

Les valeurs des attributs xpath des éléments xsd:selector et xsd:field sont des expressions XPath restreintes [Chapitre 6]. 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 '|' [Section 6.2.4] et l'opérateur de composition de chemins '/' [Section 6.2.3]. 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 [Section 6.2.1.1] autorisés dans ces expressions XPath sont les axes child::

et attribute:: dans leurs syntaxes abrégées [Section 6.7] ' ' et '@'. L'axe descendant:: peut, en outre, apparaître au début des expressions de chemins dans sa syntaxe abrégée './/'. Les filtres ne sont pas permis dans ces expressions. La contrainte suivante impose, par exemple, que tous les enfants ainsi que tous les enfants de ses enfants group aient des valeurs d'attribut id différentes.

<xsd:unique name="all.id">

<xsd:selector xpath="* | group/*"/>

<xsd:field xpath="id"/>

</xsd:unique>

5.12.2. Contraintes d'existence

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

Ces contraintes sont introduites par un élément xsd:keyref qui doit être enfant d'un élément xsd:element.

Comme pour les contraintes d'unicité, cet élément dans lequel se trouve la contrainte définit la portée de celle-ci.

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

Schémas XML

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

<!-- Unicité des attributs id des éléments chapter -->

<xsd:key name="idchapter">

<xsd:selector xpath="chapter"/>

<xsd:field xpath="@id"/>

</xsd:key>

<!-- Existence des références idref des éléments ref -->

<xsd:keyref name="idref" refer="idchapter">

<xsd:selector xpath=".//ref"/>

<xsd:field xpath="@idref"/>

</xsd:keyref>

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

5.12.3. Exemple complet

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

<?xml version="1.0" encoding="iso-8859-1"?>

<list period="P2D">

<orders>

<order date="2008-01-08" time="17:32:28">

<product serial="101-XX" number="12"/>

<product serial="102-XY" number="23"/>

<product serial="101-ZA" number="10"/>

</order>

<order date="2008-01-09" time="17:32:28">

<product serial="101-XX" number="32"/>

</order>

<order date="2008-01-09" time="17:32:29">

<product serial="101-XX" number="32"/>

</order>

</orders>

<catalog>

<product serial="101-XX">Product n° 1</product>

<product serial="101-ZA">Product n° 2</product>

<product serial="102-XY">Product n° 3</product>

<product serial="102-XA">Product n° 4</product>

</catalog>

</list>

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

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

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

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

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

<?xml version="1.0" encoding="iso-8859-1"?>

Schémas XML

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="list">

<xsd:complexType>

<xsd:sequence>

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

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

</xsd:sequence>

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

</xsd:complexType>

<!-- Unicité du couple (date,heure) -->

<xsd:unique name="dummy">

<xsd:selector xpath="orders/order"/>

<xsd:field xpath="@date"/>

<xsd:field xpath="@time"/>

</xsd:unique>

<!-- Unicité du numéro de série -->

<xsd:key name="serial">

<xsd:selector xpath="catalog/product"/>

<xsd:field xpath="@serial"/>

</xsd:key>

<!-- Existence dans le catalogue de tout produit commandé -->

<xsd:keyref name="unused" refer="serial">

<xsd:selector xpath="orders/order/product"/>

<xsd:field xpath="@serial"/>

</xsd:keyref>

</xsd:element>

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

...

5.13. Espaces de noms

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

5.13.1. Schéma sans espace de noms

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

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

5.13.2. Espace de noms cible

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

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,

Schémas XML

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="firstname" type="xsd:string"/>