Introduction
Un document XML se doit d'être bien formé. Il s'agit de
pouvoir parser le document pour en extraire l'arbre
correspondant. Mais un document XML n'est en général pas un arbre
arbitraire: il encode une donnée structurée d'un langage
particulier. Par exemple, un arbre XHTML commence obligatoirement par
une racine <html>
qui a deux fils (et uniquement deux fils)
<head>
et <body>
. Il s'agit de la grammaire (ou du
schéma) du langage XHTML.
Un schéma décrit donc
- Un vocabulaire: ensemble de noms de balises et d'attributs
- Une structure: comment les choses sont agencées les unes aux autres.
Il existe plusieurs système de descriptions de schémas, plus ou moins expressifs. Lors de la création d'un schéma avec un système de description particulier, on essaie de s'approcher autant que possible du langage souhaité, dans la mesure des possibilités offertes par le système de description.
Dans la suite de ce tutoriel, nous allons voir les systèmes
- DTD
- XML Schema
DTDs
DTD est l'abréviation de "DocType Definition".
Il s'agit du système originel, développé conjointement avec SGML, qui décrit essentiellement un vocabulaire et qui permet une description de structure d'arbre en utilisant des expressions régulières.
Syntaxe d'une DTD
Il s'agit d'une suite de balises particulières sous la forme Chaque balise définit un mot du vocabulaire. Les balises utilisées sont
<!ELEMENT ... >
pour déclarer un noeud de l'arbre XML<!ATTLIST ... >
pour déclarer les attributs possible d'un élément<!ENTITY ... >
pour déclarer des abbréviations
Les éléments
Contenu vide
Contenu quelconque
Contenu textuel (parsed character data)
Contenu composé de sous-éléments : Expression rationnelle qui définit l'organisation des sous-éléments
Contenu mixte (cas particulier des précédents)
Expressions rationelles
La syntaxe utilisée pour décrire le contenu d'un élément utilise:
- Une séquence: "," (virgule)
- Une alternative: "|"
- Une occurence quelconque (0 ou plus): "*"
- Une occurence quelconque (1 ou plus): "+"
- 0 ou 1 occurence: "?"
Par exemple, pour décrire un format d'adresse:
Les attributs
On associe une liste d'attributs à un élément de la façon suivante:
Le champs "défaut" peut être:
#IMPLIED | optionnel |
#REQUIRED | obligatoire |
#FIXED 'valeur' | valeur imposée |
'valeur' | valeur par défaut |
Le champs "type" peut être:
CDATA
: type "chaîne de caractères"
- Valeurs énumérées
ID
: identifiant XML unique dans le document
IDREF
,IDREFS
: référence à un(des) attribut(s) de type ID
NMTOKEN
,NMTOKENS
: identifiant(s) XML
Les abbréviations (ENTITY
)
Deux types d'abbréviations principales :
- Celles utilisées dans le document XML:
&abbrev;
- Celles utilisées pour l'écriture de la DTD
Le premier type d'abbréviation a comme format:
On l'utilise comme "'
", "<
"... (qui sont d'ailleurs définis exactement comme ça). Dans un document XML, on peut alors faire:
Par exemple, que produit ce document XML ? (Copiez ce texte dans un fichier "fichier.xml" puis ouvrez-le avec firefox)
La deuxième sorte d'abbréviation est uniquement utilisée en interne dans la définition d'une DTD. Elle s'utilise comme suit; Par exemple, à la place de on peut écrire Notez comment dans ce type d'abbréviation interne à la DTD on utilise "%" en place de "&".
Inclusion dans un document XML
On peut soit inclure la spécification DTD dans un document externe, ou directemenr dans le document. On peut aussi mixer les deux.
En fichier externe
On place la DTD dans un document .dtd
et on y fait référence en
donnant le noeud racine
Le mot-clé SYSTEM indique que la DTD est locale. On peut indiquer
PUBLIC
si elle est publiée sur un site internet.
En fichier interne
Fichier externe étendu en interne
Utilisé pour augmenter la syntaxe localement. Par exemple pour ajouter une abbréviation:
Attention
Un document .dtd
n'est pas d'un document XML... Donc PAS de déclaration <?xml
en entête d'un document DTD.
Limitation
Les DTDs permettent d'exprimer des contraintes assez basiques
- Liste des éléments et de leurs attributs
- Règles de structuration des éléments
Une DTD est donc très limitée:
- Impossible de typer réellement les attributs et les champs de texte
- Il ne peut y avoir deux éléments de même nom dans deux contextes différents
- Pas d'héritage possible entre éléments: notation lourde
- Pas de prise en compte des espaces de noms (vus plus tard)
XML Schema
XML Schema, ou XSD, pour XML Schema Definition, est le
schéma standard actuel pour la validation XML, recommandé par le W3C
depuis 2001. Il permet des choses beaucoup plus riche que les DTDs, et
est basé sur une syntaxe XML. Comme il s'agit d'une application
XML,il y a un espace de nom pour le XML Schema:
http://www.w3.org/2001/XMLSchema
.
C'est une norme extrèmement riche et compliquée, qui permet de faire beaucoup: héritage, support pour multiples espaces de noms, ... Elle est aussi souple pour permettre la rédaction de schémas de façon très variée, ce qui peut aussi la rendre peu lisible (et rendre sa documentation absconse).
Dans ce tutoriel, nous allons en voir un sous-ensemble gérable. Ce sous-ensemble est néanmoins suffisamment riche pour que vous puissiez après si vous le souhaitiez approfondir sans trop de problèmes.
Format d'un document
Un document XSD a pour racine un noeud schema
, dans l'espace de
nom en question. Il est à noté que les attributs sont sans espace de
nom. Il faut donc en général mettre un préfixe. Ici, on utilisera
xs
, mais vous pouvez bien sûr choisir ce que vous voulez.
Dans ce cas de figure, le XSD n'associe pas d'espace de nom au document XML créé. On peut si on veut le faire avec l'attribut targetNamespace
Sous la racine viennent, dans un ordre quelconque, des définitions:
- d'attributs
<xs:attribute ...>
- d'élément
<xs:element ...>
- de types dit simples:
<xs:simpleType ...>
- de types dit complexes:
<xs:complexType ...>
Il y a aussi la notion de content type, mais pour les besoins de ce tutoriel nous n'en parlerons pas. Il suffit juste de savoir qu'en général on peut s'en passer.
Lier un document XML à un XML Schema
Comme pour les DTDs, on souhaite pouvoir indiquer qu'un document XML
particulier est sensé valider une grammaire particulière donnée par
la XSD "monFichier.xsd
".
Le XML Schema est l'archétype de l'application XML: on va
ajouter à la racine les deux arguments
Comme décrit dans le tuto XML, l'attribut xmlns:xsi
associe au préfixe xsi
l'espace de nom en argument, ici l'espace
de nom XML Schema. Le document est donc "augmenté" avec le vocabulaire
en question. L'attribut noNamespaceSchemaLocation
est donc une
composante propre des XML Schema (comme l'atteste le préfixe), et dans
ce cas de figure indique à un validateur éventuel où aller chercher le
fichier XSD.
La notion de type
L'un des problèmes avec les DTDs était leur absence de souplesse. En particulier l'impossibilité de donner une structure aux valeurs des attributs ou au texte entre les balises.
XSD permet de le faire en proposant l'utilisation de types. On a ainsi
accès à une liste de types pré-définis, comme date
, boolean
,
string
, decimal
... Mais XSD permets aussi de définir ses
propres types, pour demander une expression régulière particulière, ou
pour imposer des bornes à un entier par exemple.
Mais de façon plus générale, le type fait aussi référence aux attributs et fils potentiels d'un élément. D'où la distinction:
- Un élément est muni d'un type simple si il n'a pas d'attributs ni de fils, uniquement du texte
- Un élément est muni d'un type complexe dans le cas contraire
- Sur ce principe, un attribut ne peut avoir qu'un type simple !
Type simple et attribut
Avec un type prédéfini
Définissons un attribut de type nonNegativeInteger
. Notez qu'il
faut de fait utiliser le préfixe pour indiquer qu'on parle bien du
type préféfini. Dans ce premier cas, il s'agit d'un noeud sans fils:
Avec un type simple explicite
On peut aussi le faire en deux fois, en définissant d'abord un type simple (et en le nommant pour pouvoir y faire référence ensuite), puis en l'utilisant. L'exemple précédant est équivalent à
On définit un nouveau type simple typeDeNombre
, et on indique
qu'il est basé sur nonNegativeInteger
avec la balise
<restriction>
.
On aurait aussi pu ne pas nommer le type et directement le placer dans
la balise <attribute>
(celle-ci n'ayant alors pas d'argument
type
):
Avec la restriction d'un type prédéfini
La balise <restriction>
peut avoir des fils pour indiquer,
justement, des restrictions. Par exemple, si on souhaite que les valeurs de l'argument soit comprises entre 42 et 84, on fera
ou de façon équivalente, en deux fois avec un type nommé
Outre minInclusive
et maxInclusive
, on peut aussi spécifier
totalDigits
, length
, enumeration
, ...
Comme la liste, ou l'union d'un autre type
Un type simple peut décrire une liste d'un certain type:
Dans ce cas, on pourra avoir l'attribut
De façon similaire, un type union est donné par exemple avec
Dans ce cas, l'attribut "temps
" peut être soit une date, soit un
entier positif non-nul.
Type simple et élément
Comme dit plus haut, on peut aussi donner un type simple à un élément. Dans ce cas, l'élément se comporte essentiellement comme un attribut: il n'a pas de fils autre qu'un champ de texte du format décrit par le type, et surtout pas d'attributs.
Par exemple,
permet de spécifier
Types complexes
Les type complexes permettent de spécifier des attributs et des fils à un noeud:
In type complexe a la forme
L'attribut mixed
indique si le noeud peut contenir un mélange de
texte et de balises. On peut donc typer quelque chose comme cela:
La description de l'agencement des fils se fait avec les balises
<all>
, <choice>
et <sequence>
.
<all>
Tous les éléments spécifiés doivent apparaitre exactement une fois,
quelque soit l'ordre. On peut ajuster la contrainte de l'ordre
d'apparition avec l'attribut minOccur="0"
si on veut uniquement
imposer "au plus une fois". Par exemple:
On impose que les fils de <all>
soit <element>
.
<choice>
Seule une des possibilité doit apparaitre en fils de l'élément. Par
exemple:
Les fils de <choice>
peuvent être <element>
, mais aussi
<choice>
et <sequence>
.
<sequence>
Pour spécifier une suite ordonnée de fils. Par exemple, en utilisant le type au dessus:
Nombre d'occurences
On peut spécifier dans un complexType
le nombre de fois que chaque composant doit apparaitre à l'aide des attributs maxOccurs
et minOccurs
.
Il peuvent apparaître en attribut de
<element>
<choice>
<all>
Nombre illimité d'éléments
Pour un nombre illimité d'éléments, on utilisera maxOccurs="unbounded"
.
Par exemple:
va valider
mais aussi
et