Les XSD offrent également la possibilité de définir des contraintes d'unicité sur la valeur de certains nœuds et de leurs attributs ainsi que des relations de type clés étrangères ou foreign key. C'est cet aspect que nous allons traité au travers d'un exemple très simple.
Prenons le fichier XML suivant, permettant de décrire une équipe de football :
<equipe nom="Olympique Lyonnais" xmlns="http://avianey.blogspot.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://avianey.blogspot.com my.xsd"> <effectif> <joueur nom="Lloris" prenom="Hugo" numeroMaillot="1"> <poste>gardien</poste> </joueur> <joueur nom="Bastos" prenom="Michel" numeroMaillot="11"> <poste>milieu</poste> </joueur> <joueur nom="Lopez" prenom="Lisandro" numeroMaillot="11"> <poste>attaquant</poste> </joueur> </effectif> <capitaine numeroMaillot="9"/> </equipe>Ce fichier XML est valide au regard du schéma XSD suivant :
<xs:schema xmlns="http://avianey.blogspot.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://avianey.blogspot.com" elementFormDefault="qualified"> <xs:element name="equipe"> <xs:complexType> <xs:sequence> <xs:element name="effectif" minOccurs="1" maxOccurs="1"> <xs:complexType> <xs:sequence> <xs:element name="joueur" minOccurs="1" maxOccurs="unbounded" type="joueur"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="capitaine" minOccurs="1" maxOccurs="1"> <xs:complexType> <xs:attribute name="numeroMaillot" use="required" type="xs:int"/> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="nom" use="required" type="xs:string"/> </xs:complexType> </xs:element> <xs:complexType name="joueur"> <xs:sequence> <xs:element name="poste" minOccurs="1" maxOccurs="1"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="gardien"/> <xs:enumeration value="défenseur"/> <xs:enumeration value="milieu"/> <xs:enumeration value="attaquant"/> </xs:restriction> </xs:simpleType> </xs:element> </xs:sequence> <xs:attribute name="nom" type="xs:string" use="required"/> <xs:attribute name="prenom" type="xs:string" use="required"/> <xs:attribute name="numeroMaillot" use="required"> <xs:simpleType> <xs:restriction base="xs:int"> <xs:minInclusive value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> </xs:schema>Ce qui n'est pas totalement satisfaisant car notre XML comporte deux joueurs avec un même numéro de maillot et parce que le capitaine est définit par référence au numéro de maillot d'un joueur qui n'existe pas !
Ajout d'une contrainte d'unicité
Nous souhaitons maintenant pouvoir imposer l'unicité des numéros de maillots entre les joueurs d'une équipe. Pour cela il faut ajouter une contrainte d'unicité sur l'attribut numeroMaillot d'une balise <joueur>. La contrainte d'unicité s'ajoute dans le schéma XSD au niveau de la déclaration d'un élément parent de l'élément sur lequel la contrainte d'unicité doit s'appliquer. La balise à utiliser est la balise <unique> se positionne à l'intérieure de la balise <element> :<xs:unique name="uniciteNumeroMaillot" > <xs:selector xpath=".//tns:joueur" /> <xs:field xpath="@numeroMaillot" /> </xs:unique>La contrainte d'unicité doit posséder un nom et contient deux fils :
- <selector>
- Expression XPATH indiquant le chemin des balises sur lesquelles la contrainte d'unicité doit s'appliquer.
- <field>
- Expression XPATH indiquant le champ ou attribut portant la contrainte. Identifiant de la valeur devant être unique.
<xs:schema xmlns="http://avianey.blogspot.com" xmlns:tns="http://avianey.blogspot.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://avianey.blogspot.com" elementFormDefault="qualified">
Ajout d'une contrainte de type clé étrangère
Maintenant qu'un même numéro de maillot ne peut pas être attribué à deux joueurs différents, nous désirons nous assurer que le capitaine de l'équipe est bien désigné par un numéro de maillot attribué à un joueur de l'équipe. Une foreign key doit donc être mise en place entre l'attribut numeroMaillot de la balise <capitaine> et celui des balises <joueur>. L'attribut numeroMaillot des balises <joueur> est dans un premier temps déclaré en tant que clé dans le schéma XSD au niveau de la déclaration d'un élément parent des balises <joueur>. La balise à utiliser est la balise <key> se positionne à l'intérieure de la balise <element> :<xs:key name="numeroMaillotCapitaine"> <xs:selector xpath=".//tns:joueur" /> <xs:field xpath="@numeroMaillot" /> </xs:key>La clé doit posséder un nom et contient deux fils :
- <selector>
- Expression XPATH indiquant le chemin des balises qui porteront les valeurs de référence.
- <field>
- Expression XPATH indiquant le champ ou l'attribut portant la valeur de référence.
<xs:keyref name="numeroMaillotCapitaineRef" refer="numeroMaillotCapitaine"> <xs:selector xpath="./tns:capitaine" /> <xs:field xpath="@numeroMaillot" /> </xs:keyref>La référence doit posséder un nom et contient deux fils :
- <selector>
- Expression XPATH indiquant le chemin des balises qui seront contraintes par la clé étrangère.
- <field>
- Expression XPATH indiquant le champ ou l'attribut dont la valeur devra être identique à une valeur de référence.
<xs:schema xmlns="http://avianey.blogspot.com" xmlns:tns="http://avianey.blogspot.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://avianey.blogspot.com" elementFormDefault="qualified"> <xs:element name="equipe"> <xs:complexType> <xs:sequence> <xs:element name="effectif" minOccurs="1" maxOccurs="1"> <xs:complexType> <xs:sequence> <xs:element name="joueur" minOccurs="1" maxOccurs="unbounded" type="joueur"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="capitaine" minOccurs="1" maxOccurs="1"> <xs:complexType> <xs:attribute name="numeroMaillot" use="required" type="xs:int"/> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="nom" use="required" type="xs:string"/> </xs:complexType> <xs:unique name="uniciteNumeroMaillot" > <xs:selector xpath=".//tns:joueur" /> <xs:field xpath="@numeroMaillot" /> </xs:unique> <xs:key name="numeroMaillotCapitaine"> <xs:selector xpath=".//tns:joueur" /> <xs:field xpath="@numeroMaillot" /> </xs:key> <xs:keyref name="numeroMaillotCapitaineRef" refer="numeroMaillotCapitaine"> <xs:selector xpath="./tns:capitaine" /> <xs:field xpath="@numeroMaillot" /> </xs:keyref> </xs:element> <xs:complexType name="joueur"> <xs:sequence> <xs:element name="poste" minOccurs="1" maxOccurs="1"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="gardien"/> <xs:enumeration value="défenseur"/> <xs:enumeration value="milieu"/> <xs:enumeration value="attaquant"/> </xs:restriction> </xs:simpleType> </xs:element> </xs:sequence> <xs:attribute name="nom" type="xs:string" use="required"/> <xs:attribute name="prenom" type="xs:string" use="required"/> <xs:attribute name="numeroMaillot" use="required"> <xs:simpleType> <xs:restriction base="xs:int"> <xs:minInclusive value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> </xs:schema>Le fichier XML initial n'est plus valide. Il faut rectifier le numéro de maillot de Lisandro Lopez pour que chaque joueur dispose de son propre numéro de maillot et que celui du capitaine correspondent à celui d'un joueur :
<equipe nom="Olympique Lyonnais" xmlns="http://avianey.blogspot.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://avianey.blogspot.com unique_key.xsd"> <effectif> <joueur nom="Lloris" prenom="Hugo" numeroMaillot="1"> <poste>gardien</poste> </joueur> <joueur nom="Bastos" prenom="Michel" numeroMaillot="11"> <poste>milieu</poste> </joueur> <joueur nom="Lopez" prenom="Lisandro" numeroMaillot="9"> <poste>attaquant</poste> </joueur> </effectif> <capitaine numeroMaillot="9"/> </equipe>