• Aucun résultat trouvé

Contraintes de cohérence

5. Schémas XML

5.12. Contraintes de cohérence

Les schémas permettent de spécifier des contraintes globales de cohérence. Celles-ci doivent être vérifiées par un document pour que celui-ci soit valide. Elles ressemblent aux contraintes des DTD portant sur les attributs des types ID, IDREF et IDREFS [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:field spécifie un champ de la valeur de la clé d'unicité. La contrainte donnée par un élément xsd:key impose que chacun des champs déterminé par les éléments xsd:field soit présent et que la valeur ainsi constituée soit unique pour les éléments sélectionnés par xsd:selector dans le contenu de l'élément définissant la portée. Au contraire, la contrainte donnée par un

Schémas XML

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

Schémas XML

<!-- Unicité des attributs id des éléments person --> <xsd:key naine="person.id">

<xsd:selector xpath="person"/> <xsd:field xpath="@id"/> </xsd:key>

</xsd:element> </xsd:schema>

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

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

5.12.1.2. Valeurs à champs multiples

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

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

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

Schémas XML <groups> <group num="1"> <person id="AC"> <firstname>Albert</firstname> <lastname>Cohen</lastname> </person> <person id="VH"> <firstname>Victor</firstname> <lastname>Hugo</lastname> </person> <person id="AC"> <firstname>Anders</firstname> <lastname>Celsius</lastname> </person> <person id="AE"> <firstname>Albert</firstname> <lastname>Einstein</lastname> </person> </group> </groups>

Il est bien sûr possible de mettre plusieurs contraintes dans un même élément. Les deux contraintes person.id

et person.names pourraient être mises simultanément dans l'élément group comme ci-dessous. <xsd:key name="person.id">

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

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

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

Le schéma précédent illustre également la différence entre les contraintes introduites par les éléments xsd:key et xsd:unique. Une contrainte introduite par xsd:key impose que tous les champs de la valeur soient présents. La contrainte person.id impose donc que l'attribut id soit présent dans chaque élément person même si cet attribut est déclaré optionnel [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,

Schémas XML

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 [Section 3.7.2] des DTD.

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

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

<!-- Unicité des attributs id des éléments chapter --> <xsd:key naine="idchapter">

<xsd:selector xpath="chapter"/> <xsd:field xpath="@id"/> </xsd:key>

<!-- Existence des références idref des éléments ref --> <xsd:keyref naine="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">

Schémas XML

<order date="2008-01-08" time="17:32:28"> <product serial="101-XX" number="12"/> <product serial="102-XY" number="23"/> <product serial="101-ZA" number="10"/> </order>

<order date="2008-01-09" time="17:32:28"> <product serial="101-XX" number="32"/> </order>

<order date="2008-01-09" time="17:32:29"> <product serial="101-XX" number="32"/> </order>

</orders> <catalog>

<product serial="101-XX">Product n° 1</product> <product serial="101-ZA">Product n° 2</product> <product serial="102-XY">Product n° 3</product> <product serial="102-XA">Product n° 4</product> </catalog>

</list>

Le schéma correspondant impose trois contraintes suivantes sur le fichier XML. 1. Deux commandes order n'ont pas la même date et la même heure.

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

3. Tous les produits référencés dans les commandes sont présents dans le catalogue. Le début de ce schéma XML est le suivant.

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

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

<xsd:complexType> <xsd:sequence>

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

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

<!-- Unicité du couple (date,heure) --> <xsd:unique name="dummy">

<xsd:selector xpath="orders/order"/> <xsd:field xpath="@date"/>

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

<!-- Unicité du numéro de série --> <xsd:key name="serial">

<xsd:selector xpath="catalog/product"/> <xsd:field xpath="@serial"/>

</xsd:key>

<!-- Existence dans le catalogue de tout produit commandé --> <xsd:keyref name="unused" refer="serial">

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

</xsd:keyref> </xsd:element>

Schémas XML

...

Documents relatifs