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
Copyright © Organization for the Advancement of Structured Information Standards [OASIS] 2001. Tous droits réservés.
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.
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.
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.
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.
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)>
]>
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>
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>
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>
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 "/>
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>
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>
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>
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.
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>
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.
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.
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>
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.
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.
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.
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.
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>
RELAX NG propose des fonctionnalités qui dépassent le cadre des DTD pour XML. En particulier, RELAX NG :
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 :
De même, RELAX NG ne décrit pas de moyen d'associer un document XML à un schéma RELAX NG.
Toute description selon RELAX Core peut être directement convertie en description RELAX NG sans perte d'information.
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>
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>
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>
Les expressions des différentes contraintes de construction de RELAX Core sont retranscrites dans RELAX NG de la façon suivante :
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>
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>
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>
RELAX NG propose des fonctionnalités manquantes à RELAX Core.
RELAX NG montre les changements suivants par rapport à TREX :