DTD versus XML Schéma
Les DTD :
- types pauvres, peu de contraintes sur les contenus
- nombre d'apparitions d'un élément à choisir entre 0 et 1
- pas de gestion des espaces de noms
- pas un format XML
Les schémas :
- utilisation et définition de types, contraintes sur les contenus
- possibilité de définir précisément le nombre d'apparitions d'un élément
- espaces de noms supportés
- format XML, parsable facilement
La base pratique
Le fichier XML :
<racine xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="monschema.xsd"> . . . </racine>
Le fichier contenant le schéma (.xsd) :
<?xml version="1.0"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> . . . </xs:schema>
La validation avec xmllint par exemple :
xmllint --schema monschema.xsd undoc.xml
Modèles de contenu et types
On distingue deux familles de types :
- les simples qui caractérisent le contenu d'un noeud textuel ou d'un attribut ;
- les complexes sont utilisés pour décrire les autres formes de contenu.
Cela nous amène à distinguer différents modèles de contenu pour un élément selon la nature de ses noeuds fils autorisés :
- vide : aucun noeud fils ;
- simple : ne contient que des noeuds textuels ;
- complexe : que des sous-éléments ;
- mixte : à la fois du texte et des sous-éléments.
Dès qu'un élément possède un attribut, il est considéré comme étant de type complexe, même si son contenu est vide ou simple.
Les types simples prédéfinis
Consulter la recommandation du W3C pour avoir la liste exhaustive des types prédéfinis. Parmi ceux-ci, citons :
- string
- decimal, integer, positiveInteger, real
- date, dateTime, duration
- ID, IDREF, ENTITY, NMTOKEN, etc. (venant des DTD)
Définir un nouveau type simple
Liste de valeurs d'un même type simple
<xs:simpleType name="telephone"> <xs:list itemType="xs:integer" /> </xs:simpleType>
Union de types simples
<xs:simpleType name="contact"> <xs:union memberTypes="adresse telephone" /> </xs:simpleType>
Restriction d'un type simple
consiste à ajouter des contraintes à un type de base, on distingue différents types de contraintes, appelés facettes, différents suivant le type qui subit la restriction.- Énumération pour les chaînes de caractères, nombres et dates
<xs:simpleType name="jourSemaine"> <xs:restriction base="xs:string"> <xs:enumeration value="lundi" /> <xs:enumeration value="mardi" /> <xs:enumeration value="mercredi" /> <xs:enumeration value="jeudi" /> <xs:enumeration value="vendredi" /> <xs:enumeration value="samedi" /> <xs:enumeration value="dimanche" /> </xs:restriction> </xs:simpleType>
- longueur fixe, minimale ou maximale des chaînes de caractères
<xs:simpleType name="telephone"> <xs:restriction base="xs:string"> <xs:length value="10" /> </xs:restriction> </xs:simpleType>
<xs:simpleType name="motdepasse"> <xs:restriction base="xs:string"> <xs:minLength value="8" /> <xs:maxLength value="20" /> </xs:restriction> </xs:simpleType>
<xs:simpleType name="telephone"> <xs:restriction base="xs:string"> <xs:length value="10" /> </xs:restriction> </xs:simpleType>
- bornes sur les entiers et les dates
aussi minExclusive et maxExclusive
<xs:simpleType name="temperature"> <xs:restriction base="xs:integer"> <xs:minInclusive value="-15" /> <xs:maxInclusive value="+35" /> </xs:restriction> </xs:simpleType>
- nombre de chiffres sur les nombre
<xs:simpleType name="codepostal"> <xs:restriction base="xs:integer"> <xs:totalDigits value="5" /> </xs:restriction> </xs:simpleType>
<xs:simpleType name="prix"> <xs:restriction base="xs:decimal"> <xs:fractionDigits value="2" /> </xs:restriction> </xs:simpleType>
- expressions régulières sur tous les types simples (?)
<xs:simpleType name="mail"> <xs:restriction base="xs:string"> <xs:pattern value=".+@.+" /> </xs:restriction> </xs:simpleType>
Définir un attribut
- utilisation de la balise <xs:attribute /> ;
- indiquer le nom de l'attribut avec l'attribut <xs:attribute name="score" /> ;
- définir le type du contenu de l'élément en utilisant l'attribut type ;
- préciser son caractère obligatoire ou optionnel (required ou optional) à l'aide de l'attribut use ;
- éventuellement, indiquer une valeur par défaut avec l'attribut default.
<xs:attribute name="score" type="xs:integer" use="required" default="0" />
Définir un élément de type simple
- utilisation de la balise <xs:element /> ;
- indiquer le nom de l'élément avec l'attribut name ;
- préciser le nombre d'apparition autorisé pour cet élément à l'aide des attributs minOccurs et maxOccurs ;
- définir le type du contenu de l'élément en utilisant l'attribut type ;
Finalement :
<xs:element name="nom" minOccurs="1" maxOccurs="1" type="xs:string" /> <xs:element name="prénom" minOccurs="1" maxOccurs="unbounded" type="xs:string" /> <xs:element name="surnom" minOccurs="0" maxOccurs="1" type="xs:string" />
Définir un élément de type complexe
on définit les sous-éléments puis les attributscontenu vide avec attribut
<xs:element name="br"> <xs:complexType> <xs:attribute name="class" type="string" /> </xs:complexType> </xs:element>
contenu simple avec attribut
extension ou restriction sur un type simple
<xs:element name="title"> <xs:complexType> <xs:simpleContent> <xs:extension base="string"> <xs:attribute name="lang" type="string" /> </xs:extension> </xs:simpleContent> </xs:complexType> </xs:element>
contenu complexe
Il s'agit à nouveau d'utiliser <xs:complexType> puis de lister les sous-éléments autorisés au sein de l'une de ces balises :
- <xs:sequence> : les sous-éléments doivent tous apparaître, dans l'ordre ;
- <xs:all> : les sous-éléments doivent tous apparaître, mais dans un ordre quelconque ;
- <xs:choice> : seulement un des sous-éléments peut apparaître, au choix.
<xs:element name="auteur"> <xs:complexType> <xs:sequence> <xs:element name="nom" minOccurs="1" maxOccurs="1" type="xs:string" /> <xs:element name="prénom" minOccurs="1" maxOccurs="unbounded" type="xs:string" /> <xs:element name="surnom" minOccurs="0" maxOccurs="1" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:element>
contenu mixte
On définit pour cela un type complexe en précisant un attribut mixed, celui-ci indiquant que du texte peu se glisser entre tous les sous-éléments autorisés.
<xs:element name="p"> <xs:complexType mixed="true"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="em" type="xs:string" /> <xs:element name="strong" type="xs:string" /> </xs:choice> </xs:complexType> </xs:element>
Stratégies d'écriture d'un schéma
En suivant l'arborescence des documents :
<?xml version="1.0"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="CHAMPIONNAT"> <xs:complexType> <xs:sequence> <xs:element name="JOURNEE" maxOccurs="38"> <xs:complexType> <xs:sequence> <xs:element name="RENCONTRE" maxOccurs="10"> <xs:complexType> <xs:attribute name="DOMICILE" type="xs:string" /> <xs:attribute name="EXTERIEUR" type="xs:string" /> <xs:attribute name="SCORED" type="xs:string" /> <xs:attribute name="SCOREE" type="xs:string" /> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="NUMERO" type="xs:integer" /> <xs:attribute name="DATE" type="xs:string" /> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="DIVISION" type="xs:integer" /> <xs:attribute name="SAISON" type="xs:string" /> </xs:complexType> </xs:element> </xs:schema>
ou à plat avec références aux éléments déjà définis :
<?xml version="1.0"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:attribute name="DOMICILE" type="xs:string" /> <xs:attribute name="EXTERIEUR" type="xs:string" /> <xs:attribute name="SCORED" type="xs:string" /> <xs:attribute name="SCOREE" type="xs:string" /> <xs:attribute name="NUMERO" type="xs:integer" /> <xs:attribute name="DATE" type="xs:string" /> <xs:attribute name="DIVISION" type="xs:integer" /> <xs:attribute name="SAISON" type="xs:string" /> <xs:element name="RENCONTRE"> <xs:complexType> <xs:attribute ref="DOMICILE" /> <xs:attribute ref="EXTERIEUR" /> <xs:attribute ref="SCORED" /> <xs:attribute ref="SCOREE" /> </xs:complexType> </xs:element> <xs:element name="JOURNEE"> <xs:complexType> <xs:sequence> <xs:element ref="RENCONTRE" maxOccurs="10" /> </xs:sequence> <xs:attribute ref="NUMERO" /> <xs:attribute ref="DATE" /> </xs:complexType> </xs:element> <xs:element name="CHAMPIONNAT"> <xs:complexType> <xs:sequence> <xs:element ref="JOURNEE" maxOccurs="38" /> </xs:sequence> <xs:attribute ref="DIVISION" /> <xs:attribute ref="SAISON" /> </xs:complexType> </xs:element> </xs:schema>
La première stratégie contraint l'élément racine, pas la seconde. La seconde est plus modulaire et permet de réutiliser des parties du schéma.
Contraintes d'unicité et de clef
On peut toujours utiliser les ID et IDREF des DTD mais c'est plus pauvre que le mécanisme offert par XML-Schémas. Celui-ci utilise (un sous-ensemble) des expressions XPath.
Unicité
<xs:element name="bibliotheque"> <xs:complexType> ... </xs:complexType> <xs:unique name="uniquelivre"> <xs:selector xpath="book" /> <xs:field xpath="isbn" /> </xs:unique> </xs:element>
on peut mettre plusieurs fields
Clés
<xs:element name="bibliotheque"> <xs:complexType> ... </xs:complexType> <xs:key name="cleflivre"> <xs:selector xpath="livre" /> <xs:field xpath="isbn" /> </xs:key> </xs:element>
pareil que unique avec présence obligatoire de la clé en plus.
Référence
<xs:keyref name="refcleflivre" refer="cleflivre"> <xs:selector xpath="citation"/> <xs:field xpath="ref" /> </xs:keyref>
Regroupements
Regrouper des attributs
<xs:attributeGroup name="attrs_photo"> <xs:attribute name="source" type="nomfichier" /> <xs:attribute name="alt" type="xs:string" /> </xs:attributeGroup> <xs:element name="identite"> <xs:complexType> <xs:attributeGroup ref="attrs_photo" /> <xs:attribute name="personne" type="xs:string" /> </xs:complexType> </xs:element> <xs:element name="paysage"> <xs:complexType> <xs:attributeGroup ref="attrs_photo" /> <xs:attribute name="lieu" type="xs:string" /> </xs:complexType> </xs:element>
Regrouper des éléments
regrouper par all, choice, ou sequence...
<xs:group name="elems_text"> <xs:choice> <xs:element ref="refacteur" /> <xs:element ref="film" /> <xs:element ref="realisateur" /> <xs:element ref="annee" /> </xs:choice> </xs:group> <xs:element name="p"> <xs:complexType mixed="true"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:group ref="elems_text" /> </xs:choice> </xs:complexType> </xs:element>