Copyright © 2005-2022 Yves MARCOUX; dernière modification de cette page : 2022-01-04.

INU3011 Documents structurés – Premier Tour d’horizon de XML

Le langage des DTD

Index de ce texteIndex général du Tour d’horizonAccueil du Tour d’horizon

Yves MARCOUX - EBSI - Université de Montréal


Table des matières

Introduction

Syntaxe générale d’une DTD

Types de contraintes

Les déclarations ELEMENT

Un exemple : des notifications d’absence

Le langage des modèles de contenu

Contenu accepté et refusé par un modèle de contenu

Syntaxe générale

(#PCDATA)

Vue d’ensemble des opérateurs

Séquence « , »

Choix « | »

Répétable « + »

Facultatif-et-répétable « * »

Facultatif « ? »

Combinaisons d’opérateurs

Modèles de contenu mixte

EMPTY

Les déclarations ATTLIST

Un exemple

Forme générale

Les différents types de valeur

Attributs et validité

Plusieurs attributs sur un même type d’élément

Un document valide en un seul fichier


Introduction

Rappelons que le langage des DTD (Document Type Definition) sert à exprimer des contraintes de forme – ou règles syntaxiques – qui doivent être respectées par certains documents XML, au-delà des règles du bien-formé. Ce langage permet à une modélisatrice de rédiger une DTD correspondant à une forme de document appropriée pour un certain contexte et/ou certaines opérations. Un document valide selon une DTD est un « contenant » qui favorise une bonne saisie de l’information et facilite son traitement.

Le langage des DTD ressemble un peu au XML bien formé, dans la mesure où une DTD se présente concrètement comme un fichier texte (toujours en UTF-8 dans ce premier tour d’horizon) et dans la mesure où les caractères < et > y jouent aussi un rôle particulier. Il n’en demeure pas moins qu’il s’agit d’un langage complètement différent.

Ce texte présente les principaux aspects de ce langage. Le texte Validité d’un document XML est un prérequis.

Syntaxe générale d’une DTD

Une DTD n’est rien d’autre qu’une suite de déclarations inscrites dans un fichier texte. Chaque déclaration est un segment contigu de caractères consécutifs, commençant par <! et se terminant par >, et qui respecte une forme particulière.

Les déclarations ne sont jamais imbriquées l’une dans l’autre, mais se suivent simplement séquentiellement.

L’ordre d’apparition des déclarations dans une DTD n’a aucune importance (sauf dans de très rares cas, qui ne sont pas couverts dans ce Premier tour d’horizon).

Notons qu’avant, entre et/ou après les déclarations, il peut y avoir des espaces blancs (espaces, sauts de ligne, tabulations, etc.) et des commentaires, qui sont simplement ignorés. Habituellement, les déclarations sont séparées entre elles par un saut de ligne ou plus, mais cette pratique vise uniquement la lisibilité de la DTD et n’est nullement obligatoire.

Les commentaires dans une DTD sont une façon très répandue de documenter un modèle XML, notamment de décrire en langue naturelle ce que l’on s’attend à retrouver dans chaque type d’élément.

Types de contraintes

Une DTD permet à une modélisatrice de formuler des contraintes (ou restrictions) sur :

Un point important du langage des DTD est que ces contraintes ne s’expriment pas par la négative (telle ou telle chose est défendue dans les documents), mais plutôt par des énoncés positifs, qui définissent ce que l’on peut ou doit retrouver dans les documents. Les contraintes exprimées ne sont jamais du genre telle chose est interdite, mais toujours du genre voici ce qu’on peut ou doit retrouver à tel ou tel endroit dans le document. Tout ce qui n’est pas explicitement permis par la DTD est refusé; c’est de cette façon que les contraintes exprimées restreignent les documents acceptés.

La modélisatrice formule les contraintes en utilisant des déclarations de deux types : ELEMENT et ATTLIST. Une déclaration ELEMENT détermine ce que l’on peut ou doit retrouver comme contenu des éléments d’un certain type, alors qu’une déclaration ATTLIST définit un attribut que l’on peut ou doit retrouver sur les éléments d’un certain type.

La modélisatrice doit rédiger une déclaration ELEMENT pour chaque type d’élément dans son modèle, et une déclaration ATTLIST pour chaque attribut qu’il est possible de retrouver sur chaque type d’élément du modèle.

Rappelez-vous qu’en marge de la DTD, la modélisatrice doit aussi décider du type (ou nom) d’élément à utiliser comme élément de plus haut niveau des documents, puisque cette information n’est pas explicitement présente dans la DTD. Une bonne pratique consiste à déclarer l’élément de plus haut niveau en premier dans la DTD, ou à l’identifier dans un commentaire au début de la DTD, mais cette pratique n’est ni obligatoire, ni universellement observée.


Les déclarations ELEMENT

Les déclarations ELEMENT permettent de définir exactement ce que chaque type d’élément peut avoir comme contenu. Avant de voir la forme générale, voyons un exemple simple qui nous permettra d’avoir une idée générale de leur fonctionnement.

Un exemple : des notifications d’absence

Supposons qu’on envisage de mettre sur pied un système informatisé pour compiler les absences d’élèves à certains cours dans une école. Le personnel est suffisamment « techno » pour qu’on demande aux professeurs de transmettre l’information sous forme de documents XML (par exemple par courriel, mais ce pourrait être aussi via un système en ligne). On veut donc définir un modèle XML (une DTD) correspondant aux documents qui seront remplis et transmis par les professeurs.

Commençons par le choix d’un nom d’élément pour l’élément de plus haut niveau des documents (qui, encore une fois, ne s’exprime pas explicitement dans la DTD). On choisit en général quelque chose de simple et direct, alors, comme il s’agit de notifications d’absence, allons-y avec notification-d-absence. Notez qu’on ne peut utiliser ni l’espace ni l’apostrophe dans un nom d’élément, de sorte que notification d’absence n’est pas un choix possible. Notez aussi qu’on utilise « notification » au singulier, puisque chaque document portera sur une seule absence.

On passe maintenant aux déclarations ELEMENT. Il s’agit de déterminer ce que chaque type d’élément dans nos documents devra contenir. Commençons par le type des éléments de plus haut niveau dans nos documents : notification-d-absence. En supposant que les seules informations nécessaires sont la date de l’absence, le nom de l’élève et le titre du cours manqué, on pourrait rédiger la déclaration ELEMENT suivante :

<!ELEMENT notification-d-absence (date-de-l-absence, nom-de-l-élève, titre-du-cours-manqué)>

Cette déclaration stipule qu’à chaque fois qu’on rencontre un élément de type notification-d-absence, son contenu doit consister en une séquence d’exactement trois sous-éléments : date-de-l-absence, nom-de-l-élève et titre-du-cours-manqué, dans cet ordre. Comme nous savons que l’élément notification-d-absence est l’élément de plus haut niveau des documents, la déclaration se trouve à fixer la forme générale du document comme tel. Concrètement, cela veut dire que les documents valides auront tous la forme suivante (abstraction faite de la déclaration DOCTYPE) :

<notification-d-absence>
   <date-de-l-absence>…</date-de-l-absence>
   <nom-de-l-élève>…</nom-de-l-élève>
   <titre-du-cours-manqué>…</titre-du-cours-manqué>
</notification-d-absence>

Comme nous venons d’introduire trois nouveaux types d’éléments, il faut aussi les déclarer. Nous aurons donc besoin de trois autres déclarations ELEMENT. Pour la date et le titre du cours, nous choisissons de simplement faire inscrire l’information en texte libre, sans utiliser de sous-éléments, alors nous aurons ces deux déclarations :

<!ELEMENT date-de-l-absence (#PCDATA)>
<!ELEMENT titre-du-cours-manqué (#PCDATA)>

Ces déclarations stipulent que les éléments des types concernés doivent toujours ne contenir que du texte et pas de sous-éléments. Concrètement, cela veut dire que, par exemple, tout élément date-de-l-absence rencontré dans nos documents aura forcément l’allure d’un bout de texte entouré des balises <date-de-l-absence> et </date-de-l-absence>, comme ceci :

<date-de-l-absence>2032-07-09</date-de-l-absence>

Il ne peut y avoir de sous-élément. Le type d’élément titre-du-cours-manqué étant déclaré de la même façon que date-de-l-absence, la même chose est vraie pour les éléments de type titre-du-cours-manqué.

Pour le nom de l’élève, pour éviter les ambiguïtés, nous choisissons de faire inscrire le prénom et le nom de famille dans deux sous-éléments distincts. Nous rédigeons donc la déclaration suivante :

<!ELEMENT nom-de-l-élève (prénom, nom-de-famille)>

qui stipule que tout élément nom-de-l-élève doit contenir une séquence de deux sous-éléments : prénom et nom-de-famille. À leur tour, ces éléments doivent être déclarés. Comme on ne souhaite pas subdiviser au-delà du prénom ou du nom de famille, les déclarations suivantes stipuleront que les éléments de ces deux types ne peuvent contenir que du texte :

<!ELEMENT prénom (#PCDATA)>
<!ELEMENT nom-de-famille (#PCDATA)>

Au final, nous avons six déclarations ELEMENT qui, ensemble, forment notre DTD :

<!ELEMENT notification-d-absence (date-de-l-absence, nom-de-l-élève, titre-du-cours-manqué)>
<!ELEMENT date-de-l-absence (#PCDATA)>
<!ELEMENT nom-de-l-élève (prénom, nom-de-famille)>
<!ELEMENT titre-du-cours-manqué (#PCDATA)>
<!ELEMENT prénom (#PCDATA)>
<!ELEMENT nom-de-famille (#PCDATA)>

Rappelons que, même si l’ordre des déclarations peut sembler important, ce n’est pas le cas et les déclarations pourraient apparaître dans n’importe quel ordre.

Supposons que ces six déclarations sont placées dans un fichier texte accessible à l’URL http://ecole.XYZ.org/absence.dtd (l’extension .dtd est celle habituellement utilisée pour une DTD, mais ce n’est pas obligatoire). Alors, voici un exemple complet de document valide selon cette DTD :

<!DOCTYPE notification-d-absence SYSTEM "http://ecole.XYZ.org/absence.dtd">
<notification-d-absence>
   <date-de-l-absence>2036-01-31</date-de-l-absence>
   <nom-de-l-élève>
      <prénom>Clotaire</prénom>
      <nom-de-famille>Euler</nom-de-famille>
   </nom-de-l-élève>
   <titre-du-cours-manqué>Mathématiques 101</titre-du-cours-manqué>
</notification-d-absence>

Dès qu’un document contient un élément qui n’a pas été déclaré dans la DTD, ou dont le contenu n’est pas conforme à la déclaration de son type, le document est invalide, c’est-à-dire non valide (l’élément fautif, de même que son contenu, sont eux aussi dits invalides). Voici quelques exemples de documents bien formés, mais invalides selon notre DTD. Assurez-vous de bien comprendre en quoi chaque exemple déroge des règles exprimées dans la DTD (dans chaque cas, la partie fautive du document est mise en rouge et expliquée dans un commentaire).

<!DOCTYPE notification-d-absence SYSTEM "http://ecole.XYZ.org/absence.dtd">
<notification-d-absence>
   <date-de-l-absence>2036-01-31</date-de-l-absence>
   <nom-de-l-élève>Clotaire Euler</nom-de-l-élève>
<!-- Le nom doit être subdivisé en sous-éléments prénom et nom-de-famille. -->
   <titre-du-cours-manqué>Mathématiques 101</titre-du-cours-manqué>
</notification-d-absence>

<!DOCTYPE notification-d-absence SYSTEM "http://ecole.XYZ.org/absence.dtd">
<notification-d-absence>
   <date-de-l-absence>2036-01-31</date-de-l-absence>
   <nom-de-l-élève>
      <prénom>Clotaire</prénom>
      <nom-de-famille>Euler</nom-de-famille>
   </nom-de-l-élève>
   <titre-du-cours-manqué>Géographie 101</titre-du-cours-manqué>
   <titre-du-cours-manqué>Mathématiques 101</titre-du-cours-manqué>
<!-- Il ne peut y avoir qu’un seul sous-élément titre-de-cours-manqué. -->

</notification-d-absence>

<!DOCTYPE notification-d-absence SYSTEM "http://ecole.XYZ.org/absence.dtd">
<notification-d-absence>
   <date-de-l-absence>2036-01-31</date-de-l-absence>
   <nom-de-l-élève>
      <nom-de-famille>Euler</nom-de-famille>
      <prénom>Clotaire</prénom>
<!-- Les sous-éléments prénom et nom-de-famille n’apparaissent pas dans le bon ordre. -->

   </nom-de-l-élève>
   <titre-du-cours-manqué>Mathématiques 101</titre-du-cours-manqué>
</notification-d-absence>

<!DOCTYPE notification-d-absence SYSTEM "http://ecole.XYZ.org/absence.dtd">
<notification-d-absence>
<!-- Il manque le sous-élément date-de-l-absence -->
   <nom-de-l-élève>
      <prénom>Clotaire</prénom>
      <nom-de-famille>Euler</nom-de-famille>
   </nom-de-l-élève>
   <titre-du-cours-manqué>Mathématiques 101</titre-du-cours-manqué>
</notification-d-absence>

<!DOCTYPE notification-d-absence SYSTEM "http://ecole.XYZ.org/absence.dtd">
<notification-d-absence>
   <date-de-l-absence>2036-01-31</date-de-l-absence>
   <nom-de-l-élève>
      <prénom>Clotaire</prénom>
      <nom-de-famille>Euler</nom-de-famille>
   </nom-de-l-élève>
   <titre-du-cours-manqué>Mathématiques <niveau>101</niveau></titre-du-cours-manqué>
<!-- L’élément titre-du-cours-manqué ne peut pas contenir de sous-élément. De plus, le type d’élément "niveau" n’est pas déclaré. -->
</notification-d-absence>

Comme on peut le constater, la DTD restreint de façon très stricte la forme que peuvent prendre les documents pour être valides.


Le langage des modèles de contenu

La forme générale d’une déclaration ELEMENT est :

<!ELEMENT type-d-élément modèle-de-contenu >

Tout ce qui suit le type (= nom) d’élément constitue donc ce qu’on appelle le modèle de contenu. Un modèle de contenu permet d’exprimer la forme générale que peut avoir le contenu d’un élément. L’effet de la déclaration est de stipuler que tout élément de type type-d-élément doit, pour être valide, avoir un contenu conforme au modèle de contenu donné.

Dans le contexte d’une DTD donnée, quand on parle du « modèle de contenu d’un élément », on veut dire le modèle de contenu figurant dans la déclaration ELEMENT de ce type d’élément.

Un document comportant une déclaration DOCTYPE (donc, qui pointe à une DTD) est valide seulement si chaque élément (y compris l’élément-document) est déclaré et a un contenu conforme à son modèle de contenu. Si un élément n’a pas été déclaré, ou a un contenu non conforme à son modèle de contenu, alors l’élément lui-même, son contenu et le document dans son entier sont tous les trois dits invalides.

Afin de créer un document valide – c’est-à-dire respectant les contraintes additionnelles définies par la modélisatrice dans sa DTD, un auteur doit donc s’assurer de placer dans chaque élément (y compris l’élément-document) un contenu conforme au modèle de contenu de cet élément.

Contenu accepté et refusé par un modèle de contenu

Un contenu qui est conforme à un modèle de contenu donné est dit accepté par ce modèle de contenu. Un contenu qui n’est pas conforme au modèle de contenu est dit refusé par lui.

Ainsi, dans notre exemple ci-dessus, on dira le modèle de contenu (prénom, nom-de-famille) refuse le contenu suivant :

REFUSÉ : <nom-de-famille>Jacques</nom-de-famille><prénom>Claude</prénom>

puisque le nom de famille vient avant le prénom, mais accepte celui-ci :

<prénom>Claude</prénom><nom-de-famille>Jacques</nom-de-famille>

Par extension, on dira qu’un type d’élément accepte ou refuse un contenu si son modèle de contenu l’accepte ou le refuse.

Syntaxe générale

Un modèle de contenu est constitué de noms d’élément, de certains mots-clés fixes (comme par exemple #PCDATA), de parenthèses et d’opérateurs (comme par exemple la virgule ,). Les différents noms d’élément, mots-clés, parenthèses et opérateurs doivent être combinés selon une certaine syntaxe et c’est pourquoi on parle du langage des modèles de contenu. Comme ce langage est utilisé à l’intérieur des déclarations ELEMENT qui, elles-mêmes font partie du langage des DTD, on peut dire que le langage des modèles de contenu est un sous-langage de celui des DTD.

À part le modèle de contenu EMPTY (défini plus loin), qui s’écrit sans parenthèses, un modèle de contenu s’écrit toujours entre parenthèses ( ). Le modèle de contenu ANY s’écrit aussi sans parenthèses, mais n’est pas traité dans ce texte.

(#PCDATA)

Nous avons déjà vu dans notre exemple le modèle de contenu (#PCDATA). Ce modèle de contenu accepte n’importe quel contenu dépourvu de sous-élément, donc constitué de texte seulement. L’acronyme PCDATA est un héritage de SGML et signifie Parsed Character Data. Sans le « # », PCDATA serait incorrectement traité comme un nom d’élément.

Le contenu vide est accepté par (#PCDATA). Il n’y a donc aucun moyen en XML d’obliger la saisie d’un texte non vide dans un élément. C’est une lacune importante qui, pour plusieurs, justifie le recours à des mécanismes de restrictions syntaxiques plus puissants que les DTD, par exemple les schémas XML du W3C. Nous en parlerons en classe.

Vue d’ensemble des opérateurs

Les autres formes de modèles de contenu couvertes dans ce texte utilisent toutes un opérateur. Il y a deux types d’opérateurs : les opérateurs de liaison et les opérateurs d’occurrence. Les opérateurs de liaison sont :

Les opérateurs d’occurrence sont :

Les signes utilisés pour représenter les opérateurs (, | ? * +) sont parfois appelés des particules.

Nous présentons d’abord ci-dessous les opérateurs dans leurs utilisations et combinaisons les plus courantes. Nous dirons ensuite quelques mots sur leur utilisation en général.

Séquence « , »

La forme de base de la séquence est :

(élément1, élément2)

élément1 et élément2 sont deux types d’éléments déclarés ailleurs dans la DTD. Ce modèle de contenu n’accepte comme contenu que les suites d’exactement deux éléments, de types élément1 et élément2, dans cet ordre. Ainsi, dans le contexte de notre exemple, comme nous avons vu, le modèle de contenu :

(prénom, nom-de-famille)

accepte ce contenu :

<prénom>Claude</prénom><nom-de-famille>Jacques</nom-de-famille>

mais refuse celui-ci :

REFUSÉ : <nom-de-famille>Jacques</nom-de-famille><prénom>Claude</prénom>

Notons qu’avant, entre et/ou après les éléments, il peut y avoir des espaces blancs (espaces, sauts de ligne, tabulations, etc.), mais aucun caractère non blanc. Il peut également y avoir des commentaires. Par exemple, ces contenus seraient aussi acceptés :

<prénom>Gonzague</prénom>⤶
<nom-de-famille>Saint Bris</nom-de-famille>

<prénom>Gonzague</prénom>⤶
¤¤<!-- Vérifier si le nom de famille prend un trait d’union ! -->
<nom-de-famille>Saint Bris</nom-de-famille>

mais pas celui-ci :

REFUSÉ :
<prénom>Gonzague</prénom> dit de
<nom-de-famille>Saint Bris</nom-de-famille>

Comme nous avons vu précédemment dans notre exemple, il est possible de lier plus de deux éléments avec la séquence, en utilisant plusieurs « , ». Ainsi, le modèle de contenu :

(Adresse, Téléphone, Courriel)

n’accepte que les suites d’exactement trois éléments Adresse, Téléphone et Courriel, dans cet ordre, par exemple :

<Adresse>…</Adresse>
<Téléphone>…</Téléphone>
<Courriel>…</Courriel>

Il faudrait bien sûr que ces types d’élément soient déclarés ailleurs dans la DTD, via autant de déclarations ELEMENT, et que les contenus que nous avons représentés par « … » soient conformes à leur modèle de contenu respectif.

Note : Dans le reste de ce texte, les points de suspension «  » symbolisent un contenu accepté par le modèle de contenu du type d’élément concerné. On suppose ce type d’élément dûment déclaré par une déclaration ELEMENT appropriée.

Notons qu’il est possible pour un même type d’élément de se retrouver plusieurs fois dans une séquence. Ainsi, le modèle de contenu suivant est tout à fait plausible :

(Adresse, Téléphone, Téléphone, Courriel)

Ce modèle de contenu pourrait, par exemple, être approprié pour un type d’élément appelé Coordonnées.

Choix « | »

La forme de base du choix est :

(élément1 | élément2)

élément1 et élément2 sont deux types d’éléments déclarés ailleurs dans la DTD. Ce modèle de contenu n’accepte comme contenu qu’un seul sous-élément, qui peut être soit de type élément1 ou élément2.

L’opérateur de choix ne figurait pas dans notre exemple précédent de DTD, mais voici un exemple d’utilisation :

<!ELEMENT requête (abonnement | désabonnement)>

Avec cette déclaration, tout élément requête doit contenir exactement un des deux sous-éléments abonnement et désabonnement. Ainsi :

<abonnement>…</abonnement>

et

<désabonnement>…</désabonnement>

seraient acceptés comme contenu de requête, mais tout autre contenu serait refusé.

Tout comme avec la séquence, il est possible de lier plus de deux éléments avec le choix, en utilisant plusieurs « | », comme dans :

(abonnement | désabonnement | plainte)

Les contenus acceptés sont toujours un seul sous-élément, mais il y a un plus grand nombre de « candidats » parmi lesquels choisir.

Répétable « + »

Il est parfois souhaitable de permettre à un élément de contenir plusieurs éléments du même type. Prenons l’exemple des descripteurs dans une notice bibliographique. On sait qu’en général, une notice bibliographique peut contenir plusieurs descripteurs. Un des choix qui s’offrent à la modélisatrice est de définir un seul élément descripteurs et de faire inscrire par les auteurs des notices tous les descripteurs dans cet unique élément. Ce qui donnerait quelque chose comme :

<descripteurs>temps de fêtes, neige, messe de minuit</descripteurs>

La déclaration appropriée pour descripteurs serait alors :

<!ELEMENT descripteurs (#PCDATA)>

Il faudrait évidemment décider d’un caractère séparateur à inscrire entre les descripteurs (dans notre exemple, la virgule) et aviser les auteurs de notices qu’ils doivent respecter cette règle d’écriture.

C’est une possibilité. Cependant, en termes de facilité de traitement de l’information, il est préférable que chaque descripteur soit balisé séparément. Si on transpose en termes de formulaire, c’est un peu comme si chaque descripteur pouvait être écrit sur sa propre ligne. On imagine facilement qu’il est plus facile de transcrire des descripteurs inscrits à raison d’un par ligne que des descripteurs entassés sur une même ligne, même séparés par une virgule.

Ce que la modélisatrice voudrait permettre est d’écrire quelque chose comme :

<descripteurs>
   <descripteur>temps de fêtes</descripteur>
   <descripteur>neige</descripteur>
   <descripteur>messe de minuit</descripteur>
</descripteurs>

C’est exactement ce que l’opérateur répétable « + » lui permet de faire. La déclaration :

<!ELEMENT descripteurs (descripteur+)>

permettra aux auteurs d’inscrire un nombre arbitraire de sous-éléments descripteur – mais au minimum un – comme contenu d’un élément descripteurs. Il faudra bien sûr déclarer aussi descripteur :

<!ELEMENT descripteur (#PCDATA)>

Facultatif-et-répétable « * »

L’opérateur facultatif-et-répétable * fonctionne exactement comme répétable, mais permet d’inscrire l’élément répétable zéro fois. Autrement dit, l’élément qui contient l’élément répétable peut être laissé vide. Ainsi, avec la déclaration suivante :

<!ELEMENT descripteurs (descripteur*)>

les auteurs peuvent encore inscrire un nombre arbitraire de sous-éléments descripteur dans un élément descripteurs (comme avec répétable), mais ils peuvent également laisser l’élément descripteurs vide, comme ceci :

<descripteurs></descripteurs>

ce que les auteurs pourraient d’ailleurs aussi écrire comme ceci :

<descripteurs/>

Facultatif « ? »

Quant à lui, l’opérateur facultatif « ? » fonctionne un peu comme facultatif-et-répétable, mais accepte au plus une occurrence de l’élément auquel il s’applique, lequel, de ce fait, n’est plus répétable. Autrement dit, l’élément est là (une seule fois) ou n’est pas là.

L’utilisation de loin la plus courante de cet opérateur est en combinaison avec la séquence. Il sert alors à indiquer qu’un des éléments d’une séquence est facultatif, c’est-à-dire qu’il peut être omis.

Pour illustrer cette utilisation, nous allons reprendre l’exemple des notifications d’absence. Imaginons que l’école souhaite permettre aux professeurs d’inscrire dans une notification le motif de l’absence, lorsqu’il est connu (par exemple, lorsque l’élève a avisé par avance le professeur de son absence). La modélisatrice doit donc permettre la saisie d’un motif, mais ne doit pas l’obliger. Pour ce faire, elle ajoutera un élément à la structure globale d’une notification, mais le rendra facultatif en lui appliquant l’opérateur « ? ». Voici ce que cela donne :

<!ELEMENT notification-d-absence (date-de-l-absence, nom-de-l-élève, titre-du-cours-manqué, motif?)>

Avec ce modèle de contenu, les professeurs peuvent ajouter ou non un sous-élément motif après le titre-du-cours-manqué. Le type d’élément motif pourrait être déclaré comme suit :

<!ELEMENT motif (#PCDATA)>

Nous avons ajouté l’élément facultatif à la fin de la séquence, mais un élément facultatif peut se trouver n’importe où dans une séquence. Ainsi, techniquement, nous aurions pu l’ajouter au tout début de la séquence :

<!ELEMENT notification-d-absence (motif?, date-de-l-absence, nom-de-l-élève, titre-du-cours-manqué)>

ou au milieu :

<!ELEMENT notification-d-absence (date-de-l-absence, nom-de-l-élève, motif?, titre-du-cours-manqué)>

Il nous semblait plus naturel de l’ajouter à la fin.

Combinaisons d’opérateurs

La combinaison d’opérateurs que nous venons de voir (séquence « , » et facultatif « ? ») n’est qu’une des possibilités. En fait, les opérateurs peuvent être combinés sans restriction, « à l’infini », comme les opérations d’addition, de soustraction, de multiplication et de division peuvent l’être en arithmétique. Plus précisément :

Règle de combinaison

Dans n’importe quel modèle de contenu correct selon les règles vues jusqu’ici, si on remplace un nom d’élément par un autre modèle de contenu correct1, on en obtient un autre, tout aussi correct.

Un exemple clarifiera le processus. Prenons le modèle de contenu suivant, donné en exemple précédemment :

(Adresse, Téléphone, Téléphone, Courriel)

La règle de combinaison dit que l’on peut remplacer n’importe quel des quatre noms d’éléments par un autre modèle de contenu correct. Supposons qu’on remplace le deuxième Téléphone par :

(Fax | Avertisseur)

on obtient :

(Adresse, Téléphone, (Fax | Avertisseur), Courriel)

La règle de combinaison nous dit que ce résultat est aussi un modèle de contenu correct.

Évidemment, le modèle de contenu obtenu n’accepte pas les mêmes contenus que les modèles de départ. Dans notre exemple, le modèle obtenu accepte les contenus des deux formes suivantes :

<Adresse>…</Adresse>
<Téléphone>…</Téléphone>
<Fax>…</Fax>
<Courriel>…</Courriel>

et :

<Adresse>…</Adresse>
<Téléphone>…</Téléphone>
<Avertisseur>…</Avertisseur>
<Courriel>…</Courriel>

1Une petite exception

La seule exception à la règle de combinaison est que (#PCDATA) ne peut pas toujours être utilisé comme modèle de remplacement. Ainsi, à partir de :

(A, B)

si on remplace le A par (#PCDATA), on obtient :

((#PCDATA), B)

qui n’est pas un modèle de contenu correct, bien que les deux modèles de départ le soient. Nous verrons à la section suivante la façon particulière de combiner (#PCDATA) avec d’autres éléments.

Combinaisons courantes

Deux des combinaisons les plus courantes sont :

choix « | » et répétable « + »

et :

choix « | » et facultatif-et-répétable « * »

Il vaut la peine d’en dire quelques mots.

On obtient une combinaison de choix « | » et répétable « + » en appliquant la règle de combinaison à partir de :

(e+)

et de n’importe quel modèle utilisant le choix, par exemple :

(A | B | C)

En remplaçant le e du premier modèle par le second modèle, on obtient :

((A | B | C)+)

Incidemment, il est permis dans ce cas d’omettre les parenthèses extérieures. On peut donc récrire le modèle résultant ainsi :

(A | B | C)+

Pour comprendre quels contenus ce nouveau modèle accepte, il faut réaliser que ce qui est répétable, c’est le choix entre les trois éléments. Pour construire adéquatement un contenu accepté par ce modèle, l’auteur d’un document fera donc une série de choix successifs (aussi nombreux qu’il le veut) entre A, B et C.

Les contenus acceptés sont donc n’importe quelle succession d’éléments A ou B ou C, dans n’importe quel ordre et en n’importe quelle quantité. Les contenus suivants (affichés un par ligne) sont donc tous des exemples de contenus acceptés :

<A>…</A>
<B>…</B>
<C>…</C>
<C>…</C> <A>…</A> <A>…</A> <C>…</C>
<C>…</C> <B>…</B> <B>…</B> <B>…</B> <A>…</A>

La combinaison choix « | » et facultatif-et-répétable « * » est tout à fait similaire, sauf que le contenu vide est aussi accepté.

Modèles de contenu mixte

Jusqu’ici, les modèles de contenu que nous avons vus acceptent soit du texte, soit des sous-éléments, mais pas un mélange de sous-éléments et de texte non blanc. En effet, à part (#PCDATA), qui n’accepte que du texte, tous les autres modèles de contenu vus jusqu’ici n’acceptent que des sous-éléments; ce peut être 0, 1 ou un nombre arbitraire de sous-éléments, mais il ne s’agit toujours que de sous-éléments. À part des commentaires et d’éventuels espaces blancs (espaces, sauts de ligne, tabulations, etc.) avant, après et/ou entre les éléments, aucun texte non blanc n’est accepté par ces modèles de contenu.

Pourtant, en XML bien formé, il est tout à fait possible pour un élément de contenir à la fois du texte non blanc et des sous-éléments. C’est ce qu’on appelle un contenu mixte. Rappelez-vous l’exemple suivant tiré du texte Qu’est-ce qu’un document XML ? :

<liste>Voici deux items:<item>1</item><item>2</item></liste>

qui donne ceci en navigateur : morpho1.png

L’élément liste contient directement deux sous-éléments item, mais également le bout de texte "Voici deux items:". L’élément a un contenu mixte. Aucun des modèles de contenu que nous pouvons construire jusqu’à maintenant n’accepte de contenu mixte. C’est justement pour pallier cette lacune que les modèles de contenu mixte existent.

Avant de voir la forme générale, voyons un exemple. En tenant pour acquis que item accepte du texte :

<!ELEMENT item (#PCDATA)>

voici comment on pourrait déclarer l’élément liste pour qu’il accepte le contenu de l’exemple :

<!ELEMENT liste (#PCDATA | item)*>

Le modèle de contenu mixte est ici (#PCDATA | item)*. La façon la plus simple d’interpréter ce modèle de contenu est de dire qu’il agit exactement comme (item)*, sauf qu’avant, après et/ou entre les éléments item, il peut y avoir non seulement des espaces blancs et des commentaires, mais du texte arbitraire.

Notez qu’à cause de la présence du facultatif-et-répétable « * » dans le modèle de contenu, il n’est pas possible de contrôler le nombre d’items dans le contenu, ni le nombre d’interstices entre les éléments où il y a du texte non blanc. Ainsi, les exemples suivants seraient aussi valides :

<liste>Voici deux items:
   <item>1</item>
   <item>2</item>
et en voici un autre:
   <item>3</item>
</liste>

<liste>Aucun item dans cette "liste" !?!</liste>

Toujours à cause du facultatif-et-répétable, un contenu vide ou ne consistant qu’en des espaces blancs (et d’éventuels commentaires) sera aussi accepté. Tous les exemples suivants seraient donc aussi valides :

<liste></liste>

<liste/>

<liste>
   <!-- Un jour, inscrire ici une série d’items… -->
</liste>

La forme générale d’un modèle de contenu mixte est :

(#PCDATA | élément1 | élément2 | … | élémentn)*

élément1, élément2, …, élémentn sont tous des éléments déclarés ailleurs dans la DTD. Ils peuvent alors tous survenir dans les contenus acceptés, dans n’importe quel ordre et en n’importe quel nombre. Comme dans notre exemple, du texte arbitraire peut se retrouver entre les éléments. Voici un modèle de contenu qui pourrait être utilisé pour un contenu textuel où, comme en HTML, certains passages peuvent être mis en emphase simple (em) ou appuyée (strong) :

(#PCDATA | em | strong)*

En supposant em et strong déclarés avec modèle de contenu (#PCDATA), ceci serait un exemple de contenu accepté :

Texte avec des <em>bouts en emphase simple</em> et des <strong>bouts en emphase appuyée</strong>.

Les modèles de contenu mixte sont les seuls cas où (#PCDATA) peut être combiné avec un opérateur ou avec des noms d’élément. Comme nous avons souligné plus tôt, la règle de combinaison ne permet pas d’utiliser librement (#PCDATA) dans un modèle de contenu quelconque.

EMPTY

Il arrive que, dans un modèle XML, un élément doive être introduit simplement pour marquer un endroit dans un document, et non pour contenir quelque chose. Un parfait exemple est l’élément hr (pour horizontal rule) en HTML, dont la seule fonction est de causer l’affichage d’une ligne horizontale à un certain endroit dans le document. L’élément lui-même n’a aucun contenu utile, seule sa position à l’intérieur du document importe.

Pour ces cas, il est possible de déclarer qu’un élément doit toujours être vide. C’est ce que fait le modèle de contenu EMPTY. Le hr de HTML pourrait par exemple être déclaré comme suit :

<!ELEMENT hr EMPTY>

Comme nous avons dit, EMPTY est un des seuls modèles de contenu s’écrivant sans parenthèses.

Un élément déclaré EMPTY ne peut contenir aucun espace blanc (espace, saut de ligne, tabulation, etc.), ni même de commentaire. La façon la plus simple pour un auteur d’écrire un tel élément est d’utiliser une balise auto-fermante. Si des balises de début et de fin distinctes sont utilisées, il faut s’assurer qu’elles se « touchent ». Ainsi, notre hr doit s’écrire :

<hr/>

ou :

<hr></hr>


Les déclarations ATTLIST

En XML bien formé, dès qu’on a une balise de début ou auto-fermante, on peut y ajouter toutes les spécifications d’attribut que l’on veut, sans restriction (à condition de ne pas spécifier deux fois le même attribut dans la même balise). En XML valide, c’est la modélisatrice qui décide quels attributs peuvent être spécifiés sur chaque type d’élément de la DTD.

Cela suppose que la modélisatrice a déterminé quelles informations elle veut voir inscrites dans le contenu des éléments, et quelles informations elle désire voir inscrites en tant que valeurs de certains attributs sur certains éléments. On dit parfois que les informations consignées comme valeurs d’attribut sont des informations complémentaires que l’on n’afficherait pas nécessairement dans une version imprimée du document, mais c’est loin d’être une règle absolue. La modélisatrice a à ce chapitre une liberté totale. Quoi qu’il en soit, nous supposons ici que cette réflexion a été faite par la modélisatrice et nous concentrons seulement sur la façon dont elle doit représenter ses choix dans sa DTD.

C’est par le truchement de déclarations ATTLIST dans sa DTD que la modélisatrice définit les restrictions qu’elle souhaite imposer à l’utilisation d’attributs.

Un exemple

Encore une fois, commençons par un exemple. Retournons aux notifications d’absence. Notre modélisatrice a déjà prévu l’élément titre-du-cours-manqué, mais elle est bien consciente que les professeurs pourraient ne pas toujours inscrire cette information exactement de la même façon. Dans l’optique de faire une compilation automatisée des données, cela poserait un problème. En effet, pour le cours « Histoire de l’art 301 », certains pourraient écrire « Hist. art 301. », d’autres « Histoire de l'art-301 », d’autres encore « Histoire de l’art - 301. », etc. Or, toutes ces variantes devraient être considérées comme identifiant le même cours. La réconciliation de toutes ces formes par un programme informatique serait pour le moins ardue.

Pour minimiser l’effet de cet éparpillement des formes, la modélisatrice décide de faire inscrire, en plus du titre, le sigle du cours, qui est moins sujet aux variations de forme. En effet, les sigles ont tous la même forme très simple et risquent moins d’être déformés que les titres. La compilation des données pourra être basée sur les sigles de cours, plutôt que sur les titres, et sera donc plus fiable.

Elle décide de faire du sigle du cours un attribut de l’élément titre-du-cours-manqué. Elle ajoutera donc à sa DTD la déclaration suivante, qui rendra possible – en fait même, obligatoire – l’utilisation d’un attribut sigle sur l’élément titre-du-cours-manqué :

<!ATTLIST titre-du-cours-manqué sigle NMTOKEN #REQUIRED>

L’effet de cette déclaration est de rendre l’attribut sigle obligatoire sur tout élément titre-du-cours-manqué. Autrement dit, dès qu’un professeur inscrit un élément titre-du-cours-manqué, il n’a pas le choix : il doit inscrire un attribut sigle dans la balise de début, sinon son document est invalide.

Ainsi, ceci serait valide :

<titre-du-cours-manqué sigle="GEO101">Géographie 101</titre-du-cours-manqué>

mais pas ceci :

INVALIDE : <titre-du-cours-manqué>Géographie 101</titre-du-cours-manqué>

Rappelez-vous que les contraintes exprimées via une DTD ne sont que des contraintes de forme. Un document valide respecte une forme imposée, mais il demeure de la responsabilité de son auteur d’inscrire les bonnes informations aux bons endroits. Si un professeur inscrit des informations incorrectes ou farfelues :

<titre-du-cours-manqué sigle='ZZZ999'>Blagues 100 limites</titre-du-cours-manqué>

l’élément est quand même valide. Une DTD (comme un formulaire) ne peut que donner au document une forme propice à la bonne saisie des informations, mais ne peut la garantir.

Dans la déclaration de notre attribut, on retrouve les mots-clés NMTOKEN et #REQUIRED. Ce dernier indique que l’attribut est obligatoire, c’est-à-dire qu’il doit être spécifié sur toute occurrence de l’élément titre-du-cours-manqué. Quant à NMTOKEN, il précise le type de valeur que l’attribut peut prendre (rappelez-vous que la valeur d’un attribut est ce qui est donné entre guillemets simples ou doubles après le signe = dans la spécification d’attribut). Essentiellement, le type NMTOKEN indique que la valeur doit être constituée d’un et un seul mot (nous verrons les règles précises plus loin). Même si l’attribut est spécifié, si sa valeur n’a pas cette forme, l’élément est invalide. Les exemples suivants seraient donc invalides :

INVALIDE : <titre-du-cours-manqué sigle="GEO 101">Géographie 101</titre-du-cours-manqué>

INVALIDE : <titre-du-cours-manqué sigle="GEO,101">Géographie 101</titre-du-cours-manqué>

Forme générale

La forme générale d’une déclaration ATTLIST couverte dans ce premier tour d’horizon est :

<!ATTLIST type-d-élément nom-d-attribut type-de-valeur obligatoire-ou-non >

Le type-d-élément identifie le type d’élément auquel l’attribut déclaré s’appliquera. Le nom-d-attribut est celui par lequel l’attribut devra être spécifié dans les balises de début (ou auto-fermantes) des éléments de type type-d-élément. Le type-de-valeur, que l’on appelle aussi type d’attribut, précise le type de valeur que l’attribut pourra prendre; nous y reviendrons plus tard.

La partie suivante, obligatoire-ou-non, indique si l’attribut est obligatoire ou non :

Les différents types de valeur

Voyons maintenant les principaux types que l’on peut spécifier comme type-de-valeur pour l’attribut déclaré.

CDATA

Le type CDATA (qui signifie Character Data) indique qu’il n’y a en fait aucune restriction sur la valeur que l’attribut peut prendre : n’importe quelle chaîne de caractères peut être spécifiée comme valeur – du moment qu’elle ne contient pas de guillemet du même type (simple ou double) que celui utilisé pour délimiter la valeur. (Les caractères « < » et « & » peuvent se retrouver dans la valeur, mais ils doivent bien sûr être représentés par les appels d’entité « &lt; » et « &amp; ».)

NMTOKEN

Le type NMTOKEN (pour Name Token), utilisé dans notre exemple ci-dessus, indique que la valeur de l’attribut doit respecter les règles d’un nom XML, sans toutefois avoir besoin de commencer par une lettre ou un caractère de soulignement _ . En particulier, la valeur peut commencer par un chiffre.

Types énumérés

Contrairement aux deux types précédents, les types énumérés ne sont pas représentés par un mot-clé. Le principe d’un type énuméré est qu’au lieu de spécifier la forme générale des valeurs acceptées, la modélisatrice va énumérer une par une toutes les valeurs acceptées. Évidemment, un type énuméré n’est applicable que si l’ensemble des valeurs possibles est fini et s’il est connu à l’avance. Une autre restriction à l’utilisation d’un type énuméré est que chacune des valeurs possibles doit avoir une forme acceptée par le type NMTOKEN.

Un type énuméré s’écrit comme suit : les différentes valeurs possibles sont inscrites sans guillemets, l’une après l’autre, séparées par une barre verticale « | », le tout entre parenthèses. Des exemples sont donnés ci-après.

Certains ensembles de valeurs se prêtent particulièrement bien à un type énuméré, par exemple les noms de mois, de jours de la semaine, ou des ensembles très restreints de valeurs numériques (par exemple, une valeur entière entre 1 et 10). Ces types énumérés s’exprimeraient respectivement comme suit :

(janvier | février | mars | avril | mai | juin | juillet | août | septembre | octobre | novembre | décembre)

(lundi | mardi | mercredi | jeudi | vendredi | samedi | dimanche)

(1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10)

Voici un exemple de déclaration ATTLIST complète avec un type énuméré :

<!ATTLIST appréciation cote (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10) #REQUIRED>

Attributs et validité

Pour les attributs de types NMTOKEN et énumérés, une valeur conforme au type est dite valide, sinon elle est invalide. Si une valeur invalide est spécifiée, on dira aussi que la spécification d’attribut (la partie attrib="valeur" dans la balise) est invalide, tout comme l’élément porteur de l’attribut fautif et le document dans son entier. Ainsi, avec la déclaration de l’attribut cote ci-dessus, l’élément suivant est invalide :

INVALIDE : <appréciation cote="11">…</appréciation>

Notons que si la valeur spécifiée contient des espaces initiaux et/ou finaux, ceux-ci sont ignorés. Ainsi, toujours avec la même déclaration, cet élément est valide :

<appréciation cote=" 5 ">…</appréciation>

Si on spécifie un attribut non déclaré, l’élément porteur de l’attribut non déclaré est invalide, tout comme le document dans son entier. Ainsi, si on tient pour acquis que cote est le seul attribut déclaré pour l’élément appréciation, l’élément suivant est invalide :

INVALIDE : <appréciation cote="3" auteur="AF">…</appréciation>

Plusieurs attributs sur un même type d’élément

Il est bien sûr possible de déclarer plusieurs attributs pour le même type d’élément. À titre d’illustration, nous allons poursuivre l’exemple du type titre-du-cours-manqué, auquel nous avons déjà ajouté un attribut sigle. Supposons que certains cours sont subdivisés en plusieurs groupes-cours, parfois jusqu’à quatre groupes. Il pourrait être intéressant de compiler les absences non seulement par cours manqué, mais également par groupe-cours. Par contre, certains cours ne sont pas subdivisés.

La modélisatrice décide donc d’ajouter un second attribut à titre-du-cours-manqué, facultatif, appelé groupe, et qui pourra prendre une valeur entière entre 1 et 4 :

<!ATTLIST titre-du-cours-manqué groupe (1 | 2 | 3 | 4) #IMPLIED>

La DTD contiendra donc deux déclarations ATTLIST mentionnant le type d’élément titre-du-cours-manqué. Toute balise de début d’un élément titre-du-cours-manqué contiendra nécessairement une spécification d’attribut pour sigle, puisque cet attribut est obligatoire, et peut-être une autre pour groupe, puisque ce dernier est facultatif. Voici un exemple valide selon ces déclarations :

<titre-du-cours-manqué sigle="GEO101" groupe="2">Géographie 101</titre-du-cours-manqué>

Rappelez-vous que l’ordre des spécifications d’attribut à l’intérieur d’une balise est sans importance. L’élément suivant serait donc strictement équivalent au précédent :

<titre-du-cours-manqué groupe="2" sigle="GEO101">Géographie 101</titre-du-cours-manqué>

Voici un autre exemple valide, cette fois sans l’attribut groupe :

<titre-du-cours-manqué sigle="GEO101">Géographie 101</titre-du-cours-manqué>

Comme pour les déclarations ELEMENT, l’ordre des déclarations ATTLIST n’a aucune importance. Elles peuvent aussi être entremêlées avec les déclarations ELEMENT de façon complètement arbitraire. Cependant, une pratique courante est de placer les déclarations ATTLIST se rapportant à un type d’élément juste après la déclaration ELEMENT de ce type d’élément.

Si on tient compte des ajouts effectués jusqu’ici, notre DTD pour les notifications d’absence comporte maintenant les neuf déclarations suivantes :

<!ELEMENT notification-d-absence (date-de-l-absence, nom-de-l-élève, titre-du-cours-manqué, motif?)>
<!ELEMENT date-de-l-absence (#PCDATA)>
<!ELEMENT nom-de-l-élève (prénom, nom-de-famille)>
<!ELEMENT titre-du-cours-manqué (#PCDATA)>
<!ATTLIST titre-du-cours-manqué sigle NMTOKEN #REQUIRED>
<!ATTLIST titre-du-cours-manqué groupe (1 | 2 | 3 | 4) #IMPLIED>
<!ELEMENT motif (#PCDATA)>
<!ELEMENT prénom (#PCDATA)>
<!ELEMENT nom-de-famille (#PCDATA)>

Pour référence, cette DTD est accessible ici. Vous pouvez utiliser le lien précédent pour créer un document valide selon cette DTD (par exemple dans oXygen, qui vous permettra de valider votre document).


Un document valide en un seul fichier

Tel qu’évoqué précédemment, le scénario naturel de déploiement d’une DTD est de la placer à un emplacement informatique accessible aux auteurs. Ce sera souvent sur le Web, mais ce pourrait aussi être sur un intranet ou même sur un simple système de fichiers. Les auteurs peuvent créer des documents valides en inscrivant l’adresse de la DTD dans la déclaration DOCTYPE de leurs documents. La DTD existe à un seul endroit et les différents documents lui font simplement référence. Il n’y a aucune réplication des déclarations. C’est le scénario habituel de déploiement d’une DTD.

Cependant, si on est en apprentissage et que l’on veut créer des documents valides simplement pour explorer le langage des DTD, travailler avec deux fichiers distincts – un pour la DTD et un pour le document XML – est une nuisance qu’il est possible d’éviter. En effet, il existe une forme particulière de la déclaration DOCTYPE qui permet d’intégrer les déclarations dans le document XML lui-même. Ce n’est pas la façon normale de travailler, puisque les déclarations incluses dans un document ne peuvent servir qu’à cet unique document, mais c’est un truc utile en apprentissage ou pendant le développement d’une DTD, alors que les changements dans les déclarations sont fréquents.

Cette forme particulière de déclaration DOCTYPE est la suivante :

<!DOCTYPE ephn [
déclarations-de-la-DTD
]>

ephn doit, comme avant, être remplacé par le nom de l’élément de plus haut niveau du document et déclarations-de-la-DTD doit être remplacé par la totalité des déclarations de la DTD (généralement, sur plusieurs lignes). Le mot-clé SYSTEM suivi de l’URL de la DTD a été troqué pour une section entre crochets [ ] contenant les déclarations. Cette section entre crochets est appelée le sous-ensemble local de déclarations du document.

Voici un exemple de document valide selon notre DTD, avec la DTD incluse dans le document lui-même (pour faciliter la lecture, nous montrons la DTD en vert) :

<!DOCTYPE notification-d-absence [
   <!ELEMENT notification-d-absence (date-de-l-absence, nom-de-l-élève, titre-du-cours-manqué, motif?)>
   <!ELEMENT date-de-l-absence (#PCDATA)>
   <!ELEMENT nom-de-l-élève (prénom, nom-de-famille)>
   <!ELEMENT titre-du-cours-manqué (#PCDATA)>
   <!ATTLIST titre-du-cours-manqué sigle NMTOKEN #REQUIRED>
   <!ATTLIST titre-du-cours-manqué groupe (1 | 2 | 3 | 4) #IMPLIED>
   <!ELEMENT motif (#PCDATA)>
   <!ELEMENT prénom (#PCDATA)>
   <!ELEMENT nom-de-famille (#PCDATA)>

]>
<notification-d-absence>
   <date-de-l-absence>2036-01-31</date-de-l-absence>
   <nom-de-l-élève>
      <prénom>Clotaire</prénom>
      <nom-de-famille>Euler</nom-de-famille>
   </nom-de-l-élève>
   <titre-du-cours-manqué sigle='MAT101'>Mathématiques 101</titre-du-cours-manqué>
</notification-d-absence>

Quand on utilise cette forme de déclaration DOCTYPE, on place habituellement les déclarations de la DTD légèrement en retrait (comme nous avons fait) pour faciliter la lecture, mais ce n’est pas obligatoire.

Une DTD ainsi incluse dans un document est dite interne. Quand la DTD est un fichier séparé des documents (le scénario « normal »), on dit que la DTD est externe.

Pour référence, ce document est disponible ici. Faites afficher la source si vous voulez voir le XML brut.

Plusieurs des exemples et exercices du cours utilisent cette forme compacte de document valide.