La DTD (Document Type Definition) définit la structure légale d'un document XML : quels éléments peuvent exister, leurs attributs, leur ordre, et comment ils peuvent être imbriqués. C'est le premier mécanisme historique de validation d'un document XML.
Les DTD présentent des avantages (simplicité, support universel) mais aussi des limitations (pas de typage de données, pas de namespaces, syntaxe non-XML). Pour ces raisons, XSD les a largement remplacées dans les applications modernes.
Le DOCTYPE lie un document XML à sa DTD. Il existe trois façons de déclarer une DTD :
La DTD est directement intégrée dans le document XML, entre crochets après DOCTYPE :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bibliotheque [
<!-- Définition des éléments -->
<!ELEMENT bibliotheque (livre+)>
<!ELEMENT livre (titre, auteur+, annee?, prix)>
<!ELEMENT titre (#PCDATA)>
<!ELEMENT auteur (#PCDATA)>
<!ELEMENT annee (#PCDATA)>
<!ELEMENT prix (#PCDATA)>
<!-- Définition des attributs -->
<!ATTLIST livre isbn ID #REQUIRED>
<!ATTLIST prix devise CDATA "EUR">
]>
<bibliotheque>
<livre isbn="L001">
<titre>Introduction XML</titre>
<auteur>Jean Dupont</auteur>
<prix devise="EUR">29.99</prix>
</livre>
</bibliotheque>
La DTD est dans un fichier externe référencé par chemin relatif ou absolu :
<!-- Référence à un fichier local -->
<!DOCTYPE bibliotheque SYSTEM "bibliotheque.dtd">
<!-- Référence à une URL -->
<!DOCTYPE bibliotheque SYSTEM "http://exemple.org/dtd/bibliotheque.dtd">
Pour les DTD standardisées avec un identifiant public (FPI - Formal Public Identifier) :
<!-- DTD XHTML -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- Structure FPI : "propriétaire//type titre//langue" -->
La déclaration <!ELEMENT> définit le contenu autorisé pour un élément.
<!ELEMENT nom_element (modele_de_contenu)>
| Modèle | Signification | Exemple |
|---|---|---|
(#PCDATA) | Texte uniquement (Parsed Character Data) | <!ELEMENT titre (#PCDATA)> |
EMPTY | Élément vide (pas de contenu) | <!ELEMENT br EMPTY> |
ANY | N'importe quel contenu (déconseillé) | <!ELEMENT container ANY> |
(a, b, c) | Séquence ordonnée (a puis b puis c) | <!ELEMENT livre (titre, auteur)> |
(a | b | c) | Choix exclusif (a OU b OU c) | <!ELEMENT contact (tel | email)> |
<!-- Élément avec texte seulement -->
<!ELEMENT nom (#PCDATA)>
<!-- Usage: <nom>Jean Dupont</nom> -->
<!-- Élément vide (auto-fermant) -->
<!ELEMENT image EMPTY>
<!-- Usage: <image src="photo.jpg"/> -->
<!-- Séquence obligatoire -->
<!ELEMENT personne (nom, prenom, email)>
<!-- nom PUIS prenom PUIS email, dans cet ordre -->
<!-- Choix exclusif -->
<!ELEMENT paiement (carte | cheque | especes)>
<!-- UN SEUL parmi les trois -->
<!-- Combinaison séquence + choix -->
<!ELEMENT commande (client, (livraison | retrait), articles)>
Les quantificateurs définissent combien de fois un élément peut apparaître.
| Symbole | Cardinalité | Équivalent | Exemple |
|---|---|---|---|
element | Exactement 1 | 1..1 | (titre) → obligatoire, une fois |
element? | 0 ou 1 | 0..1 | (avis?) → optionnel |
element+ | 1 ou plus | 1..n | (auteur+) → au moins un |
element* | 0 ou plus | 0..n | (commentaire*) → optionnel, répétable |
<!-- Une bibliothèque contient au moins un livre -->
<!ELEMENT bibliotheque (livre+)>
<!-- Un livre a un titre, 1+ auteurs, éditeur optionnel, résumé optionnel -->
<!ELEMENT livre (titre, auteur+, editeur?, resume?)>
<!-- Un catalogue peut être vide ou avoir des produits -->
<!ELEMENT catalogue (produit*)>
<!-- Groupe avec cardinalité -->
<!ELEMENT menu ((entree, plat, dessert)+)>
<!-- Un ou plusieurs groupes de (entrée + plat + dessert) -->
(a, b)* signifie "zéro ou plusieurs fois la séquence (a puis b)", pas "a optionnel et b optionnel".
(a?, b?)
La déclaration <!ATTLIST> définit les attributs d'un élément.
<!ATTLIST nom_element
nom_attribut TYPE VALEUR_PAR_DEFAUT
nom_attribut2 TYPE VALEUR_PAR_DEFAUT
...
>
| Type | Description | Exemple |
|---|---|---|
CDATA | Chaîne de caractères (texte libre) | nom CDATA |
ID | Identifiant unique dans le document | id ID |
IDREF | Référence à un ID existant | ref IDREF |
IDREFS | Liste de références (séparées par espaces) | refs IDREFS |
(v1|v2|v3) | Énumération de valeurs | statut (actif|inactif) |
NMTOKEN | Token XML (sans espaces) | code NMTOKEN |
NMTOKENS | Liste de tokens | tags NMTOKENS |
| Mot-clé | Signification | Exemple |
|---|---|---|
#REQUIRED | Attribut obligatoire | id ID #REQUIRED |
#IMPLIED | Attribut optionnel | titre CDATA #IMPLIED |
#FIXED "val" | Valeur fixe (non modifiable) | version CDATA #FIXED "1.0" |
"valeur" | Valeur par défaut si non spécifié | langue CDATA "fr" |
<!-- Attributs pour élément livre -->
<!ATTLIST livre
isbn ID #REQUIRED
titre CDATA #IMPLIED
langue (fr | en | de | es) "fr"
disponible (oui | non) "oui"
version CDATA #FIXED "2.0"
>
<!-- Utilisation correcte -->
<livre isbn="L001" langue="en" disponible="oui">...</livre>
<!-- ID et IDREF pour créer des relations -->
<!ATTLIST auteur id ID #REQUIRED>
<!ATTLIST livre auteur_ref IDREF #REQUIRED>
<auteur id="A001">Victor Hugo</auteur>
<livre isbn="L001" auteur_ref="A001">Les Misérables</livre>
ID doit être unique dans tout le documentID doit respecter les règles de nommage XML (pas de chiffre au début)IDREF doit référencer un ID existant dans le documentLe contenu mixte permet de mélanger du texte et des éléments enfants.
<!-- Définition contenu mixte -->
<!ELEMENT paragraphe (#PCDATA | gras | italique | lien)*>
<!ELEMENT gras (#PCDATA)>
<!ELEMENT italique (#PCDATA)>
<!ELEMENT lien (#PCDATA)>
<!-- Utilisation -->
<paragraphe>
Ceci est un texte avec du <gras>texte en gras</gras>
et de l'<italique>italique</italique>,
plus un <lien>lien cliquable</lien>.
</paragraphe>
#PCDATA doit être en première position|), pas la séquence* (zéro ou plus)(#PCDATA | elem1 | elem2)*Les entités permettent de définir des raccourcis réutilisables ou d'inclure du contenu externe.
<!-- Entité interne (texte de remplacement) -->
<!ENTITY auteur "Jean-Claude Dupont">
<!ENTITY copyright "© 2024 Tous droits réservés">
<!-- Utilisation dans le document -->
<livre>
<ecrit_par>&auteur;</ecrit_par>
<footer>©right;</footer>
</livre>
<!-- Entité externe (contenu d'un fichier) -->
<!ENTITY chapitre1 SYSTEM "chapitre1.xml">
<livre>&chapitre1;</livre>
<!-- Définition avec % -->
<!ENTITY % texte "(#PCDATA)">
<!ENTITY % elements_inline "gras | italique | code">
<!-- Utilisation dans la DTD avec %nom; -->
<!ELEMENT titre %texte;>
<!ELEMENT paragraphe (#PCDATA | %elements_inline;)*>
<!ENTITY nom "valeur"> → utilisée avec &nom;<!ENTITY % nom "valeur"> → utilisée avec %nom;<!-- ================================ -->
<!-- DTD pour un système de gestion de gare -->
<!-- ================================ -->
<!-- Élément racine : une gare contient des trains et des usagers -->
<!ELEMENT gare (train+, usager+)>
<!-- Un train a des voitures et un commentaire optionnel -->
<!ELEMENT train (voiture+, commentaire?)>
<!ATTLIST train
numero ID #REQUIRED
type CDATA #IMPLIED
destination CDATA #REQUIRED>
<!-- Une voiture peut avoir des réservations OU être un bar -->
<!ELEMENT voiture (resa* | bar)>
<!ATTLIST voiture
numero CDATA #REQUIRED
classe (1 | 2) "2">
<!-- Réservation : élément vide avec attributs -->
<!ELEMENT resa EMPTY>
<!ATTLIST resa
numero ID #REQUIRED
usager_id IDREF #REQUIRED
place CDATA #REQUIRED>
<!-- Bar : élément vide -->
<!ELEMENT bar EMPTY>
<!ATTLIST bar service (ouvert | ferme) "ouvert">
<!-- Commentaire : contenu mixte -->
<!ELEMENT commentaire (#PCDATA | important)*>
<!ELEMENT important (#PCDATA)>
<!-- Usager avec prénom et nom -->
<!ELEMENT usager (prenom, nom, email?)>
<!ATTLIST usager
id ID #REQUIRED
type (abonne | occasionnel) "occasionnel">
<!ELEMENT prenom (#PCDATA)>
<!ELEMENT nom (#PCDATA)>
<!ELEMENT email (#PCDATA)>
| Critère | DTD | XSD |
|---|---|---|
| Syntaxe | Syntaxe propre (non-XML) | Syntaxe XML |
| Typage | Limité (CDATA, ID...) | Riche (int, date, patterns...) |
| Namespaces | Non supportés | Supportés |
| Héritage | Non | Extension/Restriction de types |
| Cardinalité | ?, +, * | minOccurs, maxOccurs précis |
| Complexité | Simple | Plus complexe |
Testez vos connaissances sur les DTD.
Q1. Quelle cardinalité signifie "1 ou plusieurs" ?
Q2. Pour un contenu mixte, où doit se trouver #PCDATA ?
Q3. Quel type d'attribut référence un ID existant ?
Q4. Que signifie #IMPLIED pour un attribut ?
Q5. Comment déclare-t-on une entité paramétrique ?
Q6. Dans (a, b)?, combien de fois peut apparaître la séquence ?