Ce document est une traduction non officielle de la spécification RELAX NG Tutorial de OASIS datée du 3 décembre 2001. Cette version traduite peut contenir des erreurs absentes de l'original, dues à la traduction elle-même. La version originale en anglais, seule officielle et normative, se trouve à l'adresse http://relaxng.org/tutorial-20011203.html

Traduction :
Alexandre Arcouteil <lex@free.fr>
Relecture :
Hervé Agnoux
Xavier Cazin

Copyright © Organization for the Advancement of Structured Information Standards [OASIS] 2001. Tous droits réservés.

Tutoriel RELAX NG

Spécification du Comité du 3 décembre 2001

Cette version :
Spécification du Comité du 3 décembre 2001
Versions précédentes :
Spécification du Comité du 10 août 2001
Auteurs :
James Clark <jjc@jclark.com>, MURATA Makoto <EB2M-MRT@asahi-net.or.jp>

Copyright © The Organization for the Advancement of Structured Information Standards [OASIS] 2001. All Rights Reserved.

This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to OASIS, except as needed for the purpose of developing OASIS specifications, in which case the procedures for copyrights defined in the OASIS Intellectual Property Rights document must be followed, or as required to translate it into languages other than English.

The limited permissions granted above are perpetual and will not be revoked by OASIS or its successors or assigns.

This document and the information contained herein is provided on an "AS IS" basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.


Résumé

RELAX NG est un langage de schéma pour XML, basé sur [RELAX] et [TREX]. Un schéma RELAX NG spécifie des motifs qui décrivent la structure et le contenu d'un document XML. Un tel schéma RELAX NG identifie une classe de documents XML qui correspond aux documents qui se conforment à cet ensemble de motifs. Un schéma RELAX NG est en soit un document XML.

Ce document est un tutoriel de RELAX NG version 1.0.

Statut de ce Document

La publication de cette Spécification du Comité a été approuvée par le comité technique OASIS RELAX NG. C'est un document stable et consensuel issu de ce comité. Les commentaires à propos de ce document peuvent être envoyés à relax-ng-comment@lists.oasis-open.org.

Une liste d'erreurs connues dans ce document est disponible à http://www.oasis-open.org/committees/relax-ng/tutorial-20011203-errata.html.

Table des Matières

1 Premiers pas
2 Choix
3 Attributs
4 Motifs nommés
5 Typage de données
6 Énumérations
7 Listes
8 Intercalations
9 Modularité
9.1 Références à des motifs externes
9.2 Combinaisons de définitions
9.3 Fusions de grammaires
9.4 Définitions de substitution
10 Espaces de noms
10.1 Utiliser l'attribut ns
10.2 Noms qualifiés
11 Classes de noms
12 Annotations
13 Grammaires emboîtées
14 Non-restrictions
15 Renseignements complémentaires

Annexes

A Comparaison avec les DTD XML 
B Comparaison avec RELAX Core
B.1 Correspondances entre RELAX NG et RELAX Core
B.1.1 Le couple elementRule-tag
B.1.2 hedgeRule
B.1.3 attPool
B.1.4 Les contraintes de construction
B.1.5 Déclarations d'attributs
B.2 Exemples
B.2.1 Prise en compte par le modèle de contenu de la position selon les axes ancêtres / frères
B.2.2 Prise en compte de la valeur des attributs par le modèle de contenu
B.3 Fonctionnalités de RELAX NG dépassant le cadre de RELAX Core
C Comparaison avec TREX
D Changements depuis la version du 12 Juin 2001
Références

1. Premiers pas

Considérons une représentation XML basique d'un répertoire d'adresses électroniques :

<répertoire>
<carte>
<nom>John Smith</nom>
<courriel>js@exemple.com</courriel>
</carte>
<carte>
<nom>Fred Bloggs</nom>
<courriel>fb@exemple.net</courriel>
</carte>
</répertoire>

La DTD en serait la suivante :

<!DOCTYPE répertoire [
<!ELEMENT répertoire (carte*)>
<!ELEMENT carte (nom, courriel)>
<!ELEMENT nom (#PCDATA)>
<!ELEMENT courriel (#PCDATA)>
]>

Un motif RELAX NG correspondant pourrait être rédigé ainsi :

<element name="répertoire" xmlns="http://relaxng.org/ns/structure/1.0">
<zeroOrMore>
<element name="carte">
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
</element>
</zeroOrMore>
</element>

Si répertoire ne doit pas être vide, alors on utilise oneOrMore (NdT : un ou plus) au lieu de zeroOrMore (NdT : zéro ou plus):

<element name="répertoire" xmlns="http://relaxng.org/ns/structure/1.0">
<oneOrMore>
<element name="carte">
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
</element>
</oneOrMore>
</element>

Changeons maintenant ce motif pour permettre à chaque carte de contenir un élément note optionnel :

<element name="répertoire" xmlns="http://relaxng.org/ns/structure/1.0">
<zeroOrMore>
<element name="carte">
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
<optional>
<element name="note">
<text/>
</element>
</optional>
</element>
</zeroOrMore>
</element>

Notez que le motif text correspond à n'importe quel texte, y compris un texte vide. Notez aussi qu'un espace séparant des balises est ignoré quand ces dernières sont comparées à un motif.

Tous les éléments définissant un motif doivent appartenir à un espace de noms identifié par l'URI :

http://relaxng.org/ns/structure/1.0

L'exemple ci-dessus  utilise la déclaration d'espace de noms par défaut xmlns="http://relaxng.org/ns/structure/1.0". Il est également autorisé d'associer un préfixe à un espace de noms :

<rng:element name="répertoire" xmlns:rng="http://relaxng.org/ns/structure/1.0">
<rng:zeroOrMore>
<rng:element name="carte">
<rng:element name="nom">
<rng:text/>
</rng:element>
<rng:element name="courriel">
<rng:text/>
</rng:element>
</rng:element>
</rng:zeroOrMore>
</rng:element>

Pour la suite de ce document, la déclaration d'espace de noms par défaut sera omise dans les exemples.

2. Choix

Supposons maintenant que nous voulions permettre, comme alternative à nom, une distinction entre un prénom et un nom-de-famille, selon un répertoire suivant :

<répertoire>
<carte>
<prénom>John</prénom>
<nom-de-famille>Smith</nom-de-famille>
<courriel>js@exemple.com</courriel>
</carte>
<carte>
<nom>Fred Bloggs</nom>
<courriel>fb@exemple.net</courriel>
</carte>
</répertoire>

On peut utiliser le motif suivant :

<element name="répertoire">
<zeroOrMore>
<element name="carte">
<choice>
<element name="nom">
<text/>
</element>
<group>
<element name="prénom">
<text/>
</element>
<element name="nom-de-famille">
<text/>
</element>
</group>
</choice>
<element name="courriel">
<text/>
</element>
<optional>
<element name="note">
<text/>
</element>
</optional>
</element>
</zeroOrMore>
</element>

Cela correspond à la DTD suivante :

<!DOCTYPE répertoire [
<!ELEMENT répertoire (carte*)>
<!ELEMENT carte ((nom | (prénom, nom-de-famille)), courriel, note?)>
<!ELEMENT nom (#PCDATA)>
<!ELEMENT courriel (#PCDATA)>
<!ELEMENT prénom (#PCDATA)>
<!ELEMENT nom-de-famille (#PCDATA)>
<!ELEMENT note (#PCDATA)>
]>

3. Attributs

Supposons que nous voulions que l'élément carte comporte deux attributs plutôt que deux éléments fils. La DTD correspondante pourrait ressembler à ceci :

<!DOCTYPE répertoire [
<!ELEMENT répertoire (carte*)>
<!ELEMENT carte EMPTY>
<!ATTLIST carte
nom CDATA #REQUIRED
courriel CDATA #REQUIRED>
]>

Ce qui revient dans le cas de RELAX NG à une simple substitution de motifs attribute aux motifs element :

<element name="répertoire">
<zeroOrMore>
<element name="carte">
<attribute name="nom">
<text/>
</attribute>
<attribute name="courriel">
<text/>
</attribute>
</element>
</zeroOrMore>
</element>

Traditionnellement, en XML, l'ordre des attributs n'est pas significatif. RELAX NG respecte cette convention. Le motif précédent valide les éléments :

<carte nom="John Smith" courriel="js@exemple.com"/>

et

<carte courriel="js@exemple.com" nom="John Smith"/>

A contrario, l'ordre des éléments est significatif. Le motif

<element name="carte">
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
</element>

ne validerait pas 

<carte><courriel>js@exemple.com</courriel><nom>John Smith</nom></carte>

Notez qu'un élément attribute, de fait, indique la présence obligatoire de cet attribut, de même qu'un élément element indique en lui même la présence obligatoire de l'élément qu'il définit. Pour spécifier qu'un attribut est optionnel, utilisez optional comme pour element :

<element name="répertoire">
<zeroOrMore>
<element name="carte">
<attribute name="nom">
<text/>
</attribute>
<attribute name="courriel">
<text/>
</attribute>
<optional>
<attribute name="note">
<text/>
</attribute>
</optional>
</element>
</zeroOrMore>
</element>

Les motifs group et choice peuvent être appliqués au motif attribute de la même manière que pour les motifs element. Par exemple, si nous voulons permettre l'emploi d'un attribut name ou des deux attributs prénom et nom-de-famille, nous pouvons le spécifier de la même manière que nous l'aurions fait pour des éléments :

<element name="répertoire">
<zeroOrMore>
<element name="carte">
<choice>
<attribute name="nom">
<text/>
</attribute>
<group>
<attribute name="prénom">
<text/>
</attribute>
<attribute name="nom-de-famille">
<text/>
</attribute>
</group>
</choice>
<attribute name="courriel">
<text/>
</attribute>
</element>
</zeroOrMore>
</element>

Les motifs group et choice peuvent combiner des motifs element et attribute sans restriction. Par exemple, le motif suivant laisse indépendamment le choix d'un élément ou d'un attribut pour insérer nom et courriel dans une carte :

<element name="répertoire">
<zeroOrMore>
<element name="carte">
<choice>
<element name="nom">
<text/>
</element>
<attribute name="nom">
<text/>
</attribute>
</choice>
<choice>
<element name="courriel">
<text/>
</element>
<attribute name="courriel">
<text/>
</attribute>
</choice>
</element>
</zeroOrMore>
</element>

Comme toujours, l'ordre relatif des éléments est significatif, alors que cela n'est pas le cas pour les attributs. Le motif précédent validerait n'importe laquelle de ces constructions :

<carte nom="John Smith" courriel="js@exemple.com"/>
<carte courriel="js@exemple.com" nom="John Smith"/>
<carte courriel="js@exemple.com"><nom>John Smith</nom></carte>
<carte nom="John Smith"><courriel>js@exemple.com</courriel></carte>
<carte><nom>John Smith</nom><courriel>js@exemple.com</courriel></carte>

Mais pas celle-ci :

<carte><courriel>js@exemple.com</courriel><nom>John Smith</nom></carte>

car le motif relatif à carte impose que tout élément fils courriel succède à un élément fils name.

Il y a une différence entre les motifs attribute et element : <text/> est le contenu par défaut d'un motif attribute, alors qu'un motif element ne peut pas être vide. Par exemple :

<attribute name="courriel"/>

est une contraction de

<attribute name="courriel">
<text/>
</attribute>

Il pourrait sembler naturel que

<element name="x"/>

valide un élément x sans attribut ni contenu. Cependant, cela provoquerait une incohérence entre la signification d'un contenu vide pour un motif element et un motif attribute. C'est pourquoi RELAX NG ne permet pas à un motif element d'être vide. Un motif qui valide un élément sans attribut ni élément fils doit utiliser <empty/> explicitement :

<element name="répertoire">
<zeroOrMore>
<element name="carte">
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
<optional>
<element name="préfèreHTML">
<empty/>
</element>
</optional>
</element>
</zeroOrMore>
</element>

Lorsqu'un motif element accepte uniquement des attributs, il n'est pas nécessaire d'utiliser empty. Par exemple :

<element name="carte">
<attribute name="courriel">
<text/>
</attribute>
</element>

est équivalent à

<element name="carte">
<attribute name="courriel">
<text/>
</attribute>
<empty/>
</element>

4. Motifs nommés

Dans le cas d'un schéma RELAX NG complexe, il est souvent pratique de pouvoir assigner des noms à des motifs composant ce schéma. À la place de :

<element name="répertoire">
<zeroOrMore>
<element name="carte">
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
</element>
</zeroOrMore>
</element>

on peut écrire

<grammar>

<start>
<element name="répertoire">
<zeroOrMore>
<element name="carte">
<ref name="contenu-de-carte"/>
</element>
</zeroOrMore>
</element>
</start>

<define name="contenu-de-carte">
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
</define>

</grammar>

Un élément grammar a un unique élément fils start, et aucun ou plusieurs éléments fils define. Les éléments start et define contiennent des motifs. Ces motifs peuvent contenir des éléments ref qui font référence à des motifs définis par un élément define de l'élément grammar. Un motif grammar est validant par comparaison avec les motifs contenus dans l'élément start (NdT : par la suite, on pourra parler d'un motif grammar sous l'appellation "grammaire").

Nous pouvons utiliser l'élément grammar pour écrire des motifs dans un style similaire aux DTD :

<grammar>

<start>
<ref name="Répertoire"/>
</start>

<define name="Répertoire">
<element name="répertoire">
<zeroOrMore>
<ref name="Carte"/>
</zeroOrMore>
</element>
</define>

<define name="Carte">
<element name="carte">
<ref name="Nom"/>
<ref name="Courriel"/>
</element>
</define>

<define name="Nom">
<element name="nom">
<text/>
</element>
</define>

<define name="Courriel">
<element name="courriel">
<text/>
</element>
</define>

</grammar>

Les références récursives sont autorisées. Par exemple :

<define name="inline">
<zeroOrMore>
<choice>
<text/>
<element name="bold">
<ref name="inline"/>
</element>
<element name="italic">
<ref name="inline"/>
</element>
<element name="span">
<optional>
<attribute name="style"/>
</optional>
<ref name="inline"/>
</element>
</choice>
</zeroOrMore>
</define>

Néanmoins, elles doivent être exprimées à l'intérieur d'un element. Ainsi, la construction suivante n'est pas autorisée :

<define name="inline">
<choice>
<text/>
<element name="bold">
<ref name="inline"/>
</element>
<element name="italic">
<ref name="inline"/>
</element>
<element name="span">
<optional>
<attribute name="style"/>
</optional>
<ref name="inline"/>
</element>
</choice>
<optional>
<ref name="inline"/>
</optional>
</define>

5. Typage de données

RELAX NG permet aux motifs de faire référence à des types de données conformes à des définitions externes, commes celles définies par [W3C XML Schema Datatypes]. Les implémentations RELAX NG peuvent différer dans les types de données qu'elles supportent. Vous devez utiliser les types de données qui sont supportés par l'implémentation que vous souhaitez utiliser.

Le motif data valide une chaîne de caractères qui représente la valeur d'un type de données désigné. L'attribut datatypeLibrary prend pour valeur l'URI identifiant la librairie de types de données utilisée. La librairie de types de données de [W3C XML Schema Datatypes] doit être identifiée par l'URI http://www.w3.org/2001/XMLSchema-datatypes. L'attribut type spécifie le nom d'un type de données de la libraire identifiée par l'attribut datatypeLibrary. Par exemple, si une implémentation RELAX NG supportait les types de données de [W3C XML Schema Datatypes], vous pourriez utiliser :

<element name="nombre">
<data type="integer" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
</element>

Il ne serait pas commode de devoir spécifier systématiquement un attribut datatypeLibrary pour chaque élément data, c'est pourquoi RELAX NG permet à ses éléments d'hériter de la valeur de l'attribut datatypeLibrary. L'attribut datatypeLibrary peut-être spécifié pour n'importe quel élément RELAX NG. Si un élément data n'a pas d'attribut datatypeLibrary, la valeur de l'attribut datatypeLibrary de l'élément parent le plus direct sera utilisée. Typiquement, l'attribut datatypeLibrary dans l'élément racine d'un schéma RELAX NG. Par exemple :

<element name="point" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<element name="x">
<data type="double"/>
</element>
<element name="y">
<data type="double"/>
</element>
</element>

Si le fils d'un élément ou attribut se conforme à un motif data, alors tout le contenu de cet élément ou attribut doit se conformer à ce motif data. Un motif ne peut pas permettre à une partie de son contenu de se conformer à un motif data donné tout en permettant à une autre partie de se conformer à un autre . Par exemple, le motif suivant n'est pas autorisé :

<element name="incorrect">
<data type="int"/>
<element name="note">
<text/>
</element>
</element>

Cependant, celui-ci serait correct :

<element name="correct">
<data type="int"/>
<attribute name="note">
<text/>
</attribute>
</element>

Notez que ces restrictions ne s'appliquent pas au motif text.

Les types de données peuvent avoir des paramètres. Par exemple, une chaîne de caractères d'un type de données peut avoir un paramètre contrôlant sa longueur. Les paramètres applicables à des types de données particuliers sont déterminés par le vocabulaire de typage des données. Les paramètres sont spécifiés en insérant un ou plusieurs éléments param comme éléments fils d'un élément data. L'exemple suivant limite la chaîne de caractères contenue dans un élément courriel à une longueur maximum de 127 caractères :

<element name="courriel">
<data type="string">
<param name="maxLength">127</param>
</data>
</element>

6. Énumérations

Des balises peuvent contenir des attributs dont la valeur est limitée à une liste de valeurs possibles. Le motif value valide une chaîne de caractères qui a une valeur spécifiée. Par exemple :

<element name="carte">
<attribute name="nom"/>
<attribute name="courriel"/>
<attribute name="préférence-de-format">
<choice>
<value>html</value>
<value>text</value>
</choice>
</attribute>
</element>

permet à l'attribut préférence-de-format de prendre la valeur html ou text. Cela correspond à la DTD :

<!DOCTYPE carte [
<!ELEMENT carte EMPTY>
<!ATTLIST carte
nom CDATA #REQUIRED
courriel CDATA #REQUIRED
préférence-de-format (html|text) #REQUIRED>
]>

L'utilisation du motif value n'est pas réservée aux valeurs d'attributs. L'exemple suivant est autorisé :

<element name="carte">
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
<element name="préférence-de-format">
<choice>
<value>html</value>
<value>text</value>
</choice>
</element>
</element>

L'impossibilité pour les motifs data de ne valider qu'une partie du contenu d'un élément s'applique aussi au motif  value.

Par défaut, le motif value compare la chaîne de caractères qu'il contient à celles d'un document XML qu'il doit valider après que leurs espaces aient été normalisés. La normalisation des espaces consiste à fusionner les séquences d'un ou plusieurs espaces en un seul espace. Cela correspond au comportement d'un parseur XML sur un attribut déclaré autre que de type CDATA. Le motif précédent valide chacun des cas suivants :

<carte nom="John Smith" courriel="js@exemple.com" préférence-de-format="html"/>
<carte nom="John Smith" courriel="js@exemple.com" préférence-de-format=" html "/>

La façon dont le motif value compare la chaîne de caractères qu'il contient avec une chaîne d'un document peut être conditionnée en lui ajoutant un attribut type et éventuellement un attribut datatypeLibrary optionnel, qui permettent de spécifier son type de données de la même manière que pour le motif data. Un motif de chaîne de caractères valide les chaînes d'un document si elles ont le même type de données spécifié par l'attribut. Ainsi, alors que le motif data valide toute valeur d'un type de données spécifié, le motif value valide une valeur précise d'un type de données spécifié.

Si aucun élément ancêtre ne comporte d'attribut datatypeLibrary, la librairie de types de données propre à  RELAX NG est choisie par défaut. Elle fournit deux types de données, string et token. Le type de données natif token correspond à la méthode de comparaison par défaut du motif value. Le type de données natif string compare les chaînes de caractères sans aucune normalisation des espaces (autre que les fins de ligne et la normalisation des valeurs des attributs à laquelle on procède automatiquement en XML). Par exemple :

<element name="carte">
<attribute name="nom"/>
<attribute name="courriel"/>
<attribute name="préférence-de-format">
<choice>
<value type="string">html</value>
<value type="string">text</value>
</choice>
</attribute>
</element>

ne validerait pas

<carte nom="John Smith" courriel="js@exemple.com" préférence-de-format="  html  "/>

7. Listes

Le motif list décrit une séquence de segments séparés par un espace ; il contient un motif auquel une séquence de segments doit se conformer. Le motif list segmente une chaîne de caractères en une liste de chaînes de caractères, et compare la liste de chaînes de caractères résultante aux motifs contenus dans ce motif list.

Par exemple, supposons que nous voulions avoir un élément vecteur qui contienne deux nombres de type float séparés par un espace. Nous pouvons utiliser list comme suit :

<element name="vecteur">
<list>
<data type="float"/>
<data type="float"/>
</list>
</element>

Ou supposons que nous voulions que l'élément vecteur contienne un ou plusieurs nombres à virgule flottante séparés par un espace :

<element name="vecteur">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</element>

Ou que nous voulions un élément chemin contenant des nombres à virgule flottante :

<element name="chemin">
<list>
<oneOrMore>
<data type="double"/>
<data type="double"/>
</oneOrMore>
</list>
</element>

8. Intercalations

Le motif interleave permet à des éléments fils d'apparaître dans n'importe quel ordre. L'exemple suivant permet à l'élément carte de contenir les éléments nom et courriel dans un ordre quelconque :

<element name="répertoire">
<zeroOrMore>
<element name="carte">
<interleave>
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
</interleave>
</element>
</zeroOrMore>
</element>

Ce motif est appelé interleave en raison de la façon dont il fonctionne avec des motifs qui valident un ou plusieurs éléments. Supposons que nous voulions écrire un motif relatif à l'élément HTML head qui requiert exactement un élément title, au mieux un élément base, un ou plusieurs éléments style, script, link et meta, et que nous écrivions un motif grammar qui fournisse une définition de chaque élément. Alors nous pourrions concevoir le motif suivant qui définisse le modèle de contenu d'un élément head :

<define name="head">
<element name="head">
<interleave>
<ref name="title"/>
<optional>
<ref name="base"/>
</optional>
<zeroOrMore>
<ref name="style"/>
</zeroOrMore>
<zeroOrMore>
<ref name="script"/>
</zeroOrMore>
<zeroOrMore>
<ref name="link"/>
</zeroOrMore>
<zeroOrMore>
<ref name="meta"/>
</zeroOrMore>
</interleave>
</element>
</define>

Supposons que nous ayons un élément head qui contient un élément meta suivi d'un élément title, suivi d'un élément meta. Il se conforme au motif car il enchevêtre une séquence de deux éléments meta conformes au motif

      <zeroOrMore>
<ref name="meta"/>
</zeroOrMore>

avec une séquence d'un élément title conforme au motif

      <ref name="title"/>

La sémantique du motif interleave fait qu'une séquence d'éléments est conforme à un motif interleave, s'il s'agit d'un enchevêtrement de séquences qui sont conformes aux motifs fils du motif interleave. Notez qu'elle diffère en cela de celle du connecteur & en SGML : A* & B valide les séquences d'éléments A A B ou B A A mais pas A B A.

Il est un cas particulier très courant pour interleave : l'intercalation de <text/> avec un motif p constitue un motif qui valide une construction conforme au motif p mais qui permette aussi la présence de caractères dans son contenu. L'élément mixed en est l'expression abrégée.

<mixed> p </mixed>

est une abréviation de

<interleave> <text/> p </interleave>

9. Modularité

9.1. Référence à des motifs externes

Le motif externalRef peut être utilisé pour faire référence à un motif défini dans un fichier séparé. L'élément externalRef a un attribut href obligatoire qui spécifie l'URL du fichier qui contient le motif. Le motif externalRef valide les mêmes constructions que les motifs du fichier correspondant à l'URL spécifiée. Supposons par exemple, que vous disposiez d'un motif RELAX NG enregistré dans le fichier inline.rng, qui valide des contenus HTML en ligne de texte :

<grammar>
<start>
<ref name="inline"/>
</start>

<define name="inline">
<zeroOrMore>
<choice>
<text/>
<element name="code">
<ref name="inline"/>
</element>
<element name="em">
<ref name="inline"/>
</element>
<!-- etc -->
</choice>
</zeroOrMore>
</define>
</grammar>

Nous pourrions alors permettre à l'élément note de contenir des balises HTML en ligne de texte grâce à externalRef :

<element name="répertoire">
<zeroOrMore>
<element name="carte">
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
<optional>
<element name="note">
<externalRef href="inline.rng"/>
</element>
</optional>
</element>
</zeroOrMore>
</element>

Autre exemple, supposons que nous ayons deux motifs RELAX NG enregistrés sous forme de fichiers motif1.rng and motif2.rng. Le motif suivant valide tout ce qui est conforme à l'un des deux motifs précédents :

<choice>
<externalRef href="motif1.rng"/>
<externalRef href="motif2.rng"/>
</choice>

9.2. Combinaisons de définitions

Si une grammaire contient plusieurs définitions avec le même nom, alors ces définitions doivent spécifier via l'attribut combine, comment elles peuvent être combinées en une unique définition. L'attribut combine peut prendre la valeur choice ou interleave. Par exemple :

<define name="inline.class" combine="choice">
<element name="bold">
<ref name="inline"/>
</element>
</define>

<define name="inline.class" combine="choice">
<element name="italic">
<ref name="inline"/>
</element>
</define>

est équivalent à

<define name="inline.class">
<choice>
<element name="bold">
<ref name="inline"/>
</element>
<element name="italic">
<ref name="inline"/>
</element>
</choice>
</define>

Couramment, on combine des attributs au moyen de combine="interleave". Par exemple :

<grammar>

<start>
<element name="répertoire">
<zeroOrMore>
<element name="carte">
<ref name="carte.attliste"/>
</element>
</zeroOrMore>
</element>
</start>

<define name="carte.attliste" combine="interleave">
<attribute name="nom">
<text/>
</attribute>
</define>

<define name="carte.attliste" combine="interleave">
<attribute name="courriel">
<text/>
</attribute>
</define>

</grammar>

est équivalent à

<grammar>

<start>
<element name="répertoire">
<zeroOrMore>
<element name="carte">
<ref name="carte.attliste"/>
</element>
</zeroOrMore>
</element>
</start>

<define name="carte.attliste">
<interleave>
<attribute name="nom">
<text/>
</attribute>
<attribute name="courriel">
<text/>
</attribute>
</interleave>
</define>

</grammar>

qui est équivalent à

<grammar>

<start>
<element name="répertoire">
<zeroOrMore>
<element name="carte">
<ref name="carte.attliste"/>
</element>
</zeroOrMore>
</element>
</start>

<define name="carte.attliste">
<group>
<attribute name="nom">
<text/>
</attribute>
<attribute name="email">
<text/>
</attribute>
</group>
</define>

</grammar>

puisque les attributs sont combinés de la même manière avec interleave ou group.

C'est une erreur d'assigner des valeurs différentes à combine pour deux définitions ayant le même nom. Notez que l'ordre des définitions dans une grammaire n'est pas significatif.

De la même manière que des définitions, plusieurs éléments start peuvent être ainsi combinés.

9.3. Fusions de grammaires

L'élément include permet de fusionner deux grammaires. Un motif grammar peut contenir un élément fils include. Un élément include doit comprendre un attribut href qui spécifie l'URL du fichier contenant un motif grammar. Les définitions du motif grammar en référence seront incluses dans le motif grammar contenant l'élément include.

L'attribut combine est particulièrement complémentaire de include. Par exemple, supposons un schéma RELAX NG, inline.rng fournissant un motif relatif à du contenu en ligne de texte (inline), qui autorise les éléments bold et italic délibérément emboîtés :

<grammar>

<define name="inline">
<zeroOrMore>
<ref name="inline.class"/>
</zeroOrMore>
</define>

<define name="inline.class">
<choice>
<text/>
<element name="bold">
<ref name="inline"/>
</element>
<element name="italic">
<ref name="inline"/>
</element>
</choice>
</define>

</grammar>

Un autre schéma RELAX NG pourrait utiliser inline.rng et ajouter code et em au jeu d'éléments en ligne de texte, inline, comme suit :

<grammar>

<include href="inline.rng"/>

<start>
<element name="doc">
<zeroOrMore>
<element name="p">
<ref name="inline"/>
</element>
</zeroOrMore>
</element>
</start>

<define name="inline.class" combine="choice">
<choice>
<element name="code">
<ref name="inline">
</element>
<element name="em">
<ref name="inline">
</element>
</choice>
</define>

</grammar>

Ce qui serait équivalent à

<grammar>

<define name="inline">
<zeroOrMore>
<ref name="inline.class"/>
</zeroOrMore>
</define>

<define name="inline.class">
<choice>
<text/>
<element name="bold">
<ref name="inline"/>
</element>
<element name="italic">
<ref name="inline"/>
</element>
</choice>
</define>

<start>
<element name="doc">
<zeroOrMore>
<element name="p">
<ref name="inline"/>
</element>
</zeroOrMore>
</element>
</start>

<define name="inline.class" combine="choice">
<choice>
<element name="code">
<ref name="inline">
</element>
<element name="em">
<ref name="inline">
</element>
</choice>
</define>

</grammar>

qui équivaut à

<grammar>

<define name="inline">
<zeroOrMore>
<ref name="inline.class"/>
</zeroOrMore>
</define>

<define name="inline.class">
<choice>
<text/>
<element name="bold">
<ref name="inline"/>
</element>
<element name="italic">
<ref name="inline"/>
</element>
<element name="code">
<ref name="inline">
</element>
<element name="em">
<ref name="inline">
</element>
</choice>
</define>

<start>
<element name="doc">
<zeroOrMore>
<element name="p">
<ref name="inline"/>
</element>
</zeroOrMore>
</element>
</start>

</grammar>

Notez qu'il est possible que l'une des définitions associées à un même nom ne soit pas dotée de l'attribut combine. Par contre, il serait incorrect que plusieurs de ces définitions soient dans ce cas.

Le motif notAllowed est très utile pour fusionner des grammaires. Le motif notAllowed ne valide rien. De même qu'ajouter empty à group ne fait aucune différence, ajouter notAllowed à choice n'y change rien. Il est couramment utilisé pour laisser la possibilité d'inclure ultérieurement des motifs qui proposent des choix alternatifs au moyen de combine="choice". Par exemple, si inline.rng était rédigé de la sorte :

<grammar>

<define name="inline">
<zeroOrMore>
<choice>
<text/>
<element name="bold">
<ref name="inline"/>
</element>
<element name="italic">
<ref name="inline"/>
</element>
<ref name="inline.extra"/>
</choice>
</zeroOrMore>
</define>

<define name="inline.extra">
<notAllowed/>
</define>

</grammar>

Il pourrait faire l'objet de l'ajustement suivant qui permette d'accepter les éléments code et em :

<grammar>

<include href="inline.rng"/>

<start>
<element name="doc">
<zeroOrMore>
<element name="p">
<ref name="inline"/>
</element>
</zeroOrMore>
</element>
</start>

<define name="inline.extra" combine="choice">
<choice>
<element name="code">
<ref name="inline">
</element>
<element name="em">
<ref name="inline">
</element>
</choice>
</define>

</grammar>

9.4. Définitions de substitution

RELAX NG permet d'insérer des éléments define à l'intérieur d'un élément include pour indiquer que ces définitions se substituent aux définitions correspondantes du motif grammar inclus.

Imaginons un fichier répertoire.rng contenant :

<grammar>

<start>
<element name="répertoire">
<zeroOrMore>
<element name="carte">
<ref name="contenu-de-carte"/>
</element>
</zeroOrMore>
</element>
</start>

<define name="contenu-de-carte">
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
</define>

</grammar>

Supposons que nous souhaitions modifier ce motif de telle sorte que l'élément carte contienne un élément adresse-courriel au lieu d'un élément courriel. Nous pourrions remplacer la définition de contenu-de-carte comme suit :

<grammar>

<include href="répertoire.rng">

<define name="contenu-de-carte">
<element name="nom">
<text/>
</element>
<element name="adresse-courriel">
<text/>
</element>
</define>

</include>

</grammar>

Ce qui reviendrait à

<grammar>

<start>
<element name="répertoire">
<zeroOrMore>
<element name="carte">
<ref name="contenu-de-carte"/>
</element>
</zeroOrMore>
</element>
</start>

<define name="contenu-de-carte">
<element name="name">
<text/>
</element>
<element name="adresse-courriel">
<text/>
</element>
</define>

</grammar>

Un élément include peut aussi contenir un élément start, qui se substitue au start du motif de la grammaire incluse.

10. Espaces de noms

RELAX NG est sensible aux espaces de noms. Ainsi, on considère qu'un élément ou attribut possède à la fois un nom local et un URI d'espace de noms qui constituent à eux deux le nom de cet élément ou attribut.

10.1. Utilisation de l'attribut ns

Le motif element utilise un attribut ns pour spécifier l'URI relative à l'espace de noms des éléments qu'il valide. Par exemple :

<element name="foo" ns="http://www.exemple.com">
<empty/>
</element>

validerait chacune de ces constructions :

<foo xmlns="http://www.exemple.com"/>
<e:foo xmlns:e="http://www.exemple.com"/>
<exemple:foo xmlns:exemple="http://www.exemple.com"/>

mais aucune de celles ci :

<foo/>
<e:foo xmlns:e="http://WWW.exemple.COM"/>
<exemple:foo xmlns:exemple="http://www.exemple.net"/>

Laisser la valeur de l'attribut ns vide indique que l'URI de l'espace de noms est nul ou absent (comme dans le cas de l'attribut xmlns). Ainsi, le motif :

<element name="foo" ns="">
<empty/>
</element>

validerait ces constructions :

<foo xmlns=""/>
<foo/>

mais aucune de celles-ci :

<foo xmlns="http://www.exemple.com"/>
<e:foo xmlns:e="http://www.exemple.com"/>

Spécifier un attribut ns pour chaque element est dangereux et source d'erreurs ; c'est pourquoi RELAX NG permet de spécifier un espace de noms par défaut. Si un motif element ne spécifie pas de valeur d'attribut ns, alors il prend par défaut la valeur de l'attribut ns de l'ancêtre le plus direct qui ait un attribut ns, ou à défaut la valeur vide. Ainsi :

<element name="répertoire">
<zeroOrMore>
<element name="carte">
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
</element>
</zeroOrMore>
</element>

est équivalent à

<element name="répertoire" ns="">
<zeroOrMore>
<element name="carte" ns="">
<element name="nom" ns="">
<text/>
</element>
<element name="courriel" ns="">
<text/>
</element>
</element>
</zeroOrMore>
</element>

et

<element name="répertoire" ns="http://www.exemple.com">
<zeroOrMore>
<element name="carte">
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
</element>
</zeroOrMore>
</element>

est équivalent à

<element name="répertoire" ns="http://www.exemple.com">
<zeroOrMore>
<element name="carte" ns="http://www.exemple.com">
<element name="nom" ns="http://www.exemple.com">
<text/>
</element>
<element name="courriel" ns="http://www.exemple.com">
<text/>
</element>
</element>
</zeroOrMore>
</element>

Le motif attribute peut aussi comprendre un attribut ns. Mais dans ce cas, sa valeur par défaut s'applique différemment. Cela est du au fait que la recommandation XML Namespaces Recommendation n'étend pas l'espace de noms par défaut aux attributs. Si aucun attribut ns n'est spécifié dans un motif attribute, alors sa valeur par défaut est vide. Ainsi :

<element name="répertoire" ns="http://www.exemple.com">
<zeroOrMore>
<element name="carte">
<attribute name="nom"/>
<attribute name="courriel"/>
</element>
</zeroOrMore>
</element>

est équivalent à

<element name="répertoire" ns="http://www.exemple.com">
<zeroOrMore>
<element name="carte" ns="http://www.exemple.com">
<attribute name="nom" ns=""/>
<attribute name="courriel" ns=""/>
</element>
</zeroOrMore>
</element>

et validerait donc

<répertoire xmlns="http://www.exemple.com">
<carte nom="John Smith" courriel="js@exemple.com"/>
</répertoire>

ou

<exemple:répertoire xmlns:exemple="http://www.exemple.com">
<exemple:carte nom="John Smith" courriel="js@exemple.com"/>
</exemple:répertoire>

mais pas

<exemple:répertoire xmlns:exemple="http://www.exemple.com">
<exemple:carte exemple:nom="John Smith" exemple:courriel="js@exemple.com"/>
</exemple:répertoire>

10.2. Noms qualifiés

Dans le cas d'un motif validant des éléments et attributs appartenant à différents espaces de noms, une utilisation systématique de l'attribut ns entraînerait des déclarations répétées d'URI des espaces noms à l'intérieur du motif. Cette pratique est source d'erreurs et difficile à maintenir, aussi RELAX NG permet-il aux motifs element et attribute d'associer un préfixe à la valeur de l'attribut name pour en spécifier l'espace de noms. Dans ce cas, le préfixe spécifie l'URI de l'espace noms auquel il est associé, la déclaration d'espace noms s'appliquant aux motifs element ou attribute. Ainsi :

<element name="ab:répertoire" xmlns:ab="http://www.exemple.com/repertoire"
xmlns:a="http://www.exemple.com/adresse">
<zeroOrMore>
<element name="ab:carte">
<element name="a:nom">
<text/>
</element>
<element name="a:courriel">
<text/>
</element>
</element>
</zeroOrMore>
</element>

est équivalent à

<element name="répertoire" ns="http://www.exemple.com/repertoire">
<zeroOrMore>
<element name="carte" ns="http://www.exemple.com/repertoire">
<element name="nom" ns="http://www.exemple.com/adresse">
<text/>
</element>
<element name="courriel" ns="http://www.exemple.com/adresse">
<text/>
</element>
</element>
</zeroOrMore>
</element>

Si la valeur de l'attribut name d'un motif element ou attribute est affublée d'un préfixe, alors ce préfixe détermine l'URI de l'espace noms des éléments ou attributs concernés, ignorant tout autre valeur spécifiée par un quelconque attribut ns.

Notez que l'espace noms par défaut (spécifié par l'attribut xmlns) n'est pas pris en compte pour déterminer l'URI de l'espace noms des éléments et attributs validés par les motifs element et attribute.

11. Classes de noms

En général, le nom de l'élément validé par un élément element est spécifié par l'attribut name. Un élément element peut au lieu de cela démarrer par un élément spécifiant une classe de noms : name-class. Dans ce cas, le motif element ne validera que les éléments dont le nom appartient à la classe de noms spécifiée. La classe de noms la plus élémentaire est anyName qui comprend tous les noms, quel que soit leur nom local et l'URI de leur espace noms. Par exemple, le motif suivant valide tout document XML bien formé :

<grammar>

<start>
<ref name="tout-élément"/>
</start>

<define name="tout-élément">
<element>
<anyName/>
<zeroOrMore>
<choice>
<attribute>
<anyName/>
</attribute>
<text/>
<ref name="tout-élément"/>
</choice>
</zeroOrMore>
</element>
</define>

</grammar>

La classe de noms nsName comprend tous les noms ayant une URI d'espace noms correspondant à celle spécifiée par l'attribut ns, dont la valeur par défaut est celle de l'attribut ns du motif element englobant.

La classe de noms choice reconnaît tout nom compris par l'une des classes de noms qu'elle emboîte.

Les classes de noms anyName et nsName peuvent contenir une clause except. Par exemple :

<element name="carte" ns="http://www.exemple.com">
<zeroOrMore>
<attribute>
<anyName>
<except>
<nsName/>
<nsName ns=""/>
</except>
</anyName>
</attribute>
</zeroOrMore>
<text/>
</element>

Ce motif permet à un élément carte d'avoir des attributs dotés d'un nom qualifié du moment que leur espace de noms correspondant est différent de celui de l'élément carte.

Notez que le motif attribute valide un unique attribut même si ce dernier se réfère à une classe de noms qui comprend plusieurs noms. Pour valider un ou plusieurs attributs, l'élément zeroOrMore doit être utilisé.

Une classe de noms name a un nom unique. Le contenu de l'élément name en spécifie le nom de la même manière que l'attribut name d'un motif element. L'attribut ns spécifiant l'URI de l'espace de noms de la même manière que pour l'élément element.

Certains langages de shémas développent le concept de validation avec tolérance, qui ne valide que les éléments ou attributs qu'il reconnaît, c'est à dire ceux auquel correspond une définition dans un schéma donné. Nous pouvons implémenter ce concept dans RELAX NG avec les classes de noms qui utilisent except et name. Supposons, par exemple, que nous voulions permettre à un élément de contenir tout attribut avec un nom qualifié, tout en s'assurant qu'un éventuel attribut xml:space, aurait la valeur default ou preserve. Nous ne pourrions utiliser :

<element name="exemple">
<zeroOrMore>
<attribute>
<anyName/>
</attribute>
</zeroOrMore>
<optional>
<attribute name="xml:space">
<choice>
<value>default</value>
<value>preserve</value>
</choice>
</attribute>
</optional>
</element>

car un attribut xml:space avec une valeur autre que default ou preserve serait reconnu et validé par

    <attribute>
<anyName/>
</attribute>

alors même qu'elle ne serait pas valide selon :

    <attribute name="xml:space">
<choice>
<value>default</value>
<value>preserve</value>
</choice>
</attribute>

La solution est d'utiliser name de concert avec except :

<element name="exemple">
<zeroOrMore>
<attribute>
<anyName>
<except>
<name>xml:space</name>
</except>
</anyName>
</attribute>
</zeroOrMore>
<optional>
<attribute name="xml:space">
<choice>
<value>default</value>
<value>preserve</value>
</choice>
</attribute>
</optional>
</element>

Notez que l'élément define ne peut contenir une classe de noms. Il ne peut contenir qu'un motif.

12. Annotations

Si un élément RELAX NG contient un attribut ou élément fils dont l'URI de l'espace de noms diffère de celle de RELAX NG, alors cet attribut ou élément est ignoré. Ainsi, vous pouvez ajouter simplement des commentaires à un motif RELAX NG en utilisant un attribut ou élément associé à un espace noms différent :

<element name="répertoire" xmlns="http://relaxng.org/ns/structure/1.0" xmlns:a="http://www.exemple.com/commentaire">
<zeroOrMore>
<element name="carte">
<a:documentation>Informations sur une adresse de courriel.</a:documentation>
<element name="nom">
<text/>
</element>
<element name="courriel">
<text/>
</element>
</element>
</zeroOrMore>
</element>

RELAX NG fournit aussi un élément div qui permet d'annoter un groupe de définitions dans une grammaire. Par exemple, vous pourriez envisager de scinder les définitions d'une grammaire en modules :

<grammar xmlns:m="http://www.exemple.com/module">

<div m:name="inline">

<define name="code"> motif </define>
<define name="em"> motif </define>
<define name="var"> motif </define>

</div>

<div m:name="block">

<define name="p"> motif </define>
<define name="ul"> motif </define>
<define name="ol"> motif </define>

</div>

</grammar>

Cela permet de générer facilement des variantes d'une grammaire en se basant sur une sélection de modules.

Une spécification compagnon, RELAX NG DTD Compatibility [Compatibilité], définit des annotations qui implémentent certaines spécificités des DTD XML.

13. Grammaires emboîtées

Il n'y a aucune limitation à l'emboîtement de grammaires. Un motif ref fait référence à la définition de la grammar de parenté la plus directe. L'élément parentRef permet aussi de sortir de la grammaire en cours et fait référence à une définition de la grammaire parente de la grammaire en cours.

Considérez le problème de la conception d'un motif de tables. Un motif de tables ne s'intéresse qu'à leur structure et pas au contenu de chacune des cellules. Créons d'abord un motif RELAX NG table.rng comme suit :

<grammar>

<define name="cell.contenu">
<notAllowed/>
</define>

<start>
<element name="table">
<oneOrMore>
<element name="tr">
<oneOrMore>
<element name="td">
<ref name="cell.contenu"/>
</element>
</oneOrMore>
</element>
</oneOrMore>
</element>
</start>

</grammar>

Tout motif qui inclut table.rng doit redéfinir cell.contenu. En emboîtant un motif grammar contenant un motif parentRef, le motif englobant peut redéfinir cell.contenu selon un motif défini dans la grammaire englobant, ce qui équivaut à importer le motif depuis la grammaire parente vers la grammaire emboîtée :

<grammar>

<start>
<element name="doc">
<zeroOrMore>
<choice>
<element name="p">
<ref name="inline"/>
</element>
<grammar>
<include href="table.rng">
<define name="cell.contenu">
<parentRef name="inline"/>
</define>
</include>
</grammar>
</choice>
</zeroOrMore>
</element>
</start>

<define name="inline">
<zeroOrMore>
<choice>
<text/>
<element name="em">
<ref name="inline"/>
</element>
</choice>
</zeroOrMore>
</define>

</grammar>

Dans un cas aussi élémentaire, emboîter les grammaires n'a évidemment aucun intérêt : nous aurions pu simplement inclure table.rng à l'intérieur de l'élément grammar. Toujours est-il, qu'en incluant une grammaire ayant de nombreuses définitions, l'emboîtement permet d'éviter les risques de conflits de noms entre la grammaire englobante et la grammaire incluse.

14. Non-restrictions

RELAX NG ne nécessite pas des motifs qui soient "déterministes" ou "sans ambiguïtés".

Supposons écrire un répertoire de courriels en HTML, tout en utilisant les attributs class pour en spécifier la structure :

<element name="html">
<element name="head">
<element name="title">
<text/>
</element>
</element>
<element name="body">
<element name="table">
<attribute name="class">
<value>répertoire</value>
</attribute>
<oneOrMore>
<element name="tr">
<attribute name="class">
<value>carte</value>
</attribute>
<element name="td">
<attribute name="class">
<value>nom</value>
</attribute>
<interleave>
<text/>
<optional>
<element name="span">
<attribute name="class">
<value>prénom</value>
</attribute>
<text/>
</element>
</optional>
<optional>
<element name="span">
<attribute name="class">
<value>nom-de-famille</value>
</attribute>
<text/>
</element>
</optional>
</interleave>
</element>
<element name="td">
<attribute name="class">
<value>courriel</value>
</attribute>
<text/>
</element>
</element>
</oneOrMore>
</element>
</element>
</element>

Ce motif validerait un document XML comme celui-ci :

<html>
<head>
<title>exemple de répertoire</title>
</head>
<body>
<table class="répertoire">
<tr class="carte">
<td class="nom">
<span class="prénom">John</span>
<span class="nom-de-famille">Smith</span>
</td>
<td class="courriel">js@exemple.com</td>
</tr>
</table>
</body>
</html>

mais pas comme celui-là :

<html>
<head>
<title>exemple de répertoire</title>
</head>
<body>
<table class="répertoire">
<tr class="carte">
<td class="nom">
<span class="prénom">John</span>
<!-- Note the incorrect class attribute -->
<span class="prénom">Smith</span>
</td>
<td class="courriel">js@exemple.com</td>
</tr>
</table>
</body>
</html>

15. Renseignements complémentaires

La spécification finale de RELAX NG est [RELAX NG].

A. Comparaison avec les DTD XML

RELAX NG propose des fonctionnalités qui dépassent le cadre des DTD pour XML. En particulier, RELAX NG :

  • utilise une syntaxe XML pour représenter des schémas ;
  • supporte le typage des données ;
  • intègre les attributs dans les modèles de contenu ;
  • supporte les espaces de noms XML ;
  • supporte les contenus non ordonnés ;
  • supporte la dépendance au contexte des modèles de contenu.

La validation des ID/IDREF n'est pas fournie par RELAX NG ; Néanmoins, elle est fournie par une spécification compagnon, RELAX NG DTD Compatibility [Compatibilité]. Un support complet des contrôles d'intégrité est prévu dans une spécification future.

RELAX NG ne supporte pas certaines fonctionnalités des DTD XML qui impliquent des modifications de l'infoset d'un document XML. En particulier, RELAX NG :

  • ne permet pas de spécifier les valeurs par défaut des attributs. Néanmoins, cela est rendu possible par le document RELAX NG DTD Compatibility [Compatibilité]
  • ne permet pas de définir des entités ;
  • ne permet pas de définir des notations ;
  • ne spécifie pas quand un espace est significatif.

De même, RELAX NG ne décrit pas de moyen d'associer un document XML à un schéma RELAX NG.

B. Comparaison avec RELAX Core

Toute description selon RELAX Core peut être directement convertie en description RELAX NG sans perte d'information.

B.1. Correspondances entre RELAX NG et RELAX Core

B.1.1. Le couple elementRule-tag

Un élément elementRule et son élément référent tag peuvent être systématiquement retranscrits en un élément define contenant un élément fils element.

Soit par exemple un couple elementRule-tag propre à RELAX Core comme suit :

<elementRule role="foo" label="bar">
hedge model
</elementRule>
<tag role="foo" name="baz">
attribute declarations
</tag>

et sa retranscription suivante selon RELAX NG :

<define name="bar">
<element name="baz">
hedge model
attribute declarations
</element>
</define>

B.1.2. hedgeRule

Un élément hedgeRule est retranscrit en un élément define contenant les declarations d'attributs.

Soit par exemple un élément hedgeRule propre à RELAX Core comme suit :

<hedgeRule label="bar">
hedge model
</hedgeRule>

et sa retranscription suivante selon RELAX NG :

<define name="bar">
hedge model
</define>

B.1.3. attPool

Soit par exemple un élément attPool propre à RELAX Core comme suit :

<attPool role="foo">
attribute declarations
</attPool>

et sa retranscription suivante selon RELAX NG :

<define name="foo">
attribute declarations
</define>

B.1.4. Les contraintes de construction

Les expressions des différentes contraintes de construction de RELAX Core sont retranscrites dans RELAX NG de la façon suivante :

  1. occurs="*" dans RELAX Core correspond à <zeroOrMore>...</zeroOrMore> ;
  2. occurs="+" dans RELAX Core correspond à <oneOrMore>...</oneOrMore> ;
  3. occurs="?" dans RELAX Core correspond à <optional>...</optional> ;
  4. <mixed>...</mixed> dans RELAX Core correspond à <mixed>...</mixed> ;
  5. <ref label="..."/> dans RELAX Core correspond à <ref name="..."/> ;
  6. <hedgeRef label="..."/> dans RELAX Core correspond à <ref name="..."/>.

B.1.5. Déclarations d'attributs

Les deux langages utilisent attribute. Cependant, dans RELAX Core, un attribute sans required="true" peut-être considéré comme ayant une valeur par défaut alors que dans RELAX NG, ce type d'attribut est déclaré par un élément attribute compris dans un élément optional.

Une déclaration d'un élément obligatoire dans RELAX Core est effectuée comme suit :

<attribute name="foo" type="integer" required="true"/>

Ce qui équivaut dans RELAX NG à :

<attribute name="foo">
<data type="integer"/>
</attribute>

Une déclaration d'un attribut optionnel dans RELAX Core est effectuée comme suit :

<attribute name="foo" type="integer"/>

Ce qui équivaut dans RELAX NG à :

<optional>
<attribute name="foo">
<data type="integer"/>
</attribute>
</optional>

B.2. Exemples

B.2.1. Prise en compte par le modèle de contenu de la position selon les axes ancêtres / frères

Cet exemple est une réécriture de celui de STEP 7 dans "HOW TO RELAX". Contrairement à tous les autres, le premier paragraphe ne peut pas contenir de notes de bas de page.

<grammar>
<start>
<element name="doc">
<ref name="para-sans-notes-de-bas-de-page"/>
<zeroOrMore>
<ref name="para-avec-notes-de-bas-de-page"/>
</zeroOrMore>
</element>
</start>

<define name="para-sans-notes-de-bas-de-page">
<element name="para">
<text/>
</element>
</define>

<define name="para-avec-notes-de-bas-de-page">
<element name="para">
<mixed>
<zeroOrMore>
<element name="fnote">
<text/>
</element>
</zeroOrMore>
</mixed>
</element>
</define>

</grammar>

Ce motif validerait le document suivant :

<doc><para/><para><fnote/></para></doc>

Mais pas celui-ci :

<doc><para><fnote/></para></doc>

B.2.2. Prise en compte de la valeur des attributs par le modèle de contenu

Cet exemple est une réécriture de celui de STEP 8 dans "HOW TO RELAX". Ce motif définit différents modèles de contenu pour la même balise div selon la valeur de son attribut class.

<grammar>

<start>
<element name="html">
<zeroOrMore>
<ref name="section"/>
</zeroOrMore>
</element>
</start>

<define name="section">
<element name="div">
<attribute name="class"><value>section</value></attribute>
<zeroOrMore>
<element name="para">
<text/>
</element>
</zeroOrMore>
<zeroOrMore>
<ref name="sous-section"/>
</zeroOrMore>
</element>
</define>

<define name="sous-section">
<element name="div">
<attribute name="class"><value>sous-section</value></attribute>
<zeroOrMore>
<element name="para">
<text/>
</element>
</zeroOrMore>
</element>
</define>

</grammar>

Ce motif validerait le document suivant :

<html>
<div class="section">
<para/>
<div class="sous-section">
<para/>
</div>
</div>
<div class="section">
<div class="sous-section">
<para/>
</div>
</div>
</html>

Mais pas celui-ci :

<html>
<div class="sous-section">
<para/>
<div class="section">
<para/>
</div>
</div>
</html>

B.3. Fonctionnalités de RELAX NG dépassant le cadre de RELAX Core

RELAX NG propose des fonctionnalités manquantes à RELAX Core.

  1. espaces de noms : Comme RELAX Core est sensé être utilisé conjointement à RELAX Namespace, il ne supporte pas directement les espaces de noms alors que RELAX NG offre un tel support. RELAX Namespace sera modifié de manière à pouvoir être utilisé avec RELAX NG ;
  2. combinaisons de element et attribute: RELAX Core ne permet pas ce type de combinaison mais fournit à la place deux types de constructions élémentaires : elementRule/hedgeRule et tag/attPool ;
  3. classes de noms : RELAX Core ne définit pas de classe de noms mais des dictionnaires de noms ;
  4. interleave: RELAX Core ne propose aucun mécanisme d'intercalation ;
  5. librairies de types de données : RELAX Core supporte XML Schema Part 2 mais n'accepte aucune autre libraire de types de données ;
  6. define dans include: RELAX Core ne permet pas ce type de redéfinition ;
  7. list : RELAX Core ne supporte pas ce type de structures de chaînes de caractères ;
  8. data dans choice : Dans RELAX Core, le modèle de construction de elementRule est une référence à un type de données ou une expression sans référence à aucun type de données.

C. Comparaison avec TREX

RELAX NG montre les changements suivants par rapport à TREX :

  1. le motif concur a été supprimé ;
  2. le motif string a été remplacé par le motif value ;
  3. le motif anyString a été renommé text ;
  4. l'URI de l'espace de noms est différent ;
  5. les motifs doivent avoir un nom qualifié ;
  6. les types de données anonymes ont été supprimés ;
  7. le motif data peut avoir des paramètres spécifiés par des éléments fils param ;
  8. le motif list permettant de valider des listes de segments séparés par des espaces a été ajouté ;
  9. les valeurs replace et group pour l'attribut combine ont été supprimées ;
  10. un élément include dans une grammaire, peut contenir des éléments define qui se substituent aux définitions importées ;
  11. la restriction selon laquelle des définitions combinées au moyen de l'attribut combine doivent provenir de fichiers différents a été supprimée ;
  12. un élément div peut être utilisé pour grouper des définition à l'intérieur d'un élément grammar ;
  13. l'élément include sous son usage à l'intérieur d'un motif quelconque à été renommé externalRef. Ainsi un élément include ne peut apparaître qu'en tant que fils d'un élément grammar ;
  14. l'attribut parent d'un élément ref a été remplacé par un nouvel élément parentRef ;
  15. l'attribut type d'un élément data est un nom non-qualifié : l'élément data utilise l'attribut datatypeLibrary plutôt que ns pour identifier l'espace de noms d'un type de données ;
  16. un élément start ne peut pas avoir d'attribut name ;
  17. un élément attribute ne peut pas avoir d'attribut global ;
  18. les classes de noms not et difference ont été remplacées par except ;
  19. un élément data ne peut pas avoir d'élément fils except.

D. Changements depuis la version du 12 Juin 2001

  1. key et keyRef sont supprimés ; un support de ID et IDREF est maintenant fourni par une spécification compagnon, RELAX NG DTD Compatibility Annotations [Compatibilité]
  2. difference et not sont remplacés par except.
  3. Un élément start ne peut plus avoir d'attribut name.
  4. Un élément attribute ne peut plus avoir d'attribut global.

Références

Compatibilité
James Clark, Makoto MURATA, editors. RELAX NG DTD Compatibility. OASIS, 2001.
RELAX
MURATA Makoto. RELAX (Regular Language description for XML). INSTAC (Information Technology Research and Standardization Center), 2001.
RELAX NG
James Clark, Makoto MURATA, editors. RELAX NG Specification. OASIS, 2001.
TREX
James Clark. TREX - Tree Regular Expressions for XML. Thai Open Source Software Center, 2001.
W3C XML Schema Datatypes
Paul V. Biron, Ashok Malhotra, editors. XML Schema Part 2: Datatypes. W3C (World Wide Web Consortium), 2001.