Génération de documents avec XML


Version : 0.1
Date : 25 août 2000
Auteur : Éric Jacoboni

Table des matières


Cet article présente les différents étapes de création d'un document XML et de la génération de documents en différents formats à partir de lui.

1. Termes d'utilisation

La redistribution du code source (XML), modifié ou non, et compilé (HTML, PostScript, etc.) est soumise aux conditions suivantes:

  1. Le copyright ci-dessus, la présente liste de conditions et l'avertissement qui la suit doivent figurer dans le code source.
  2. Le code source distribué sous forme compilée doit faire apparaître le copyright ci-dessus, la présente liste de conditions et l'avertissement qui la suit.

CE DOCUMENT EST FOURNI « TEL QUEL » ET IL N'EST DONNÉ AUCUNE GARANTIE, IMPLICITE OU EXPLICITE, QUANT À SON UTILISATION COMMERCIALE, PROFESSIONNELLE OU AUTRE. L'AUTEUR NE PEUT EN AUCUN CAS ÊTRE TENU POUR RESPONSABLE DE QUELQUE DOMMAGE OU PRÉJUDICE DIRECT, INDIRECT, SECONDAIRE OU ACCESSOIRE (Y COMPRIS LES PERTES FINANCIÈRES DUES AU MANQUE À GAGNER, À L'INTERRUPTION D'ACTIVITÉS, OU LA PERTE D'INFORMATIONS ET AUTRES) DÉCOULANT DE L'UTILISATION DE LA DOCUMENTATION OU DE L'IMPOSSIBILITÉ D'UTILISER CELLE-CI, ET DONT L'UTILISATEUR ACCEPTE L'ENTIÈRE RESPONSABILITÉ.


2. Introduction

Pour produire des documents comme celui-ci, j'utilisais jusqu'alors soit LaTeX soit SGML avec les DTDs linuxdoc ou DocBook.

L'avantage de XML est qu'il permet de définir les balises dont on a besoin : on n'est plus obligé de connaître les balises du « langage », comme c'est le cas avec HTML et DocBook par exemple. Reste évidemment à expliquer ensuite ce qu'il convient de faire de ces balises.

Bien que je ne sois pas un expert en la matière, j'ai jugé utile d'expliquer en quoi XML pouvait faciliter la vie de ceux qui, comme moi, écrivent des documents amenés à être publiés sur le Web, ou sous forme d'articles imprimés ou de transparents. Cet article n'est donc pas un cours sur XML : vous trouverez dans la section Références des liens vous permettant d'en connaître les moindres détails. Par contre, parmi cette foison de présentations, assez académiques, il faut le dire, j'ai eu du mal à trouver les informations pratiques permettant de résoudre mon problème : cet article est donc une synthèse de ma compréhension actuelle.

Le problème que je m'étais fixé était le suivant : écrire un document quelconque en XML en utilisant les balises adéquates pour celui-ci, et créer le programme de transformation de ce document en un format utilisable par tous : HTML, PS ou PDF. La transformation en ces deux derniers formats nécessite une transformation préalable en dvi car je ne sais pas produire directement du PostScript!

Pour ce faire, il existe une pléthore d'outils, la plupart nécessitent Java ce qui m'a fait les écarter car je n'aime pas Java et la nécessité de l'installer pour passer de XML à, par exemple, HTML me semble excessif. Encore une fois, je veux que le document et les outils permettant de le transformer puissent être reconnus sur un maximum de machines sans nécessiter l'installation de logiciels exigeants en ressources.

À part Java, il existe également des modules Perl, qui ont l'avantage d'être moins imposants en termes d'espace disque. Perl étant installé sur tout système sérieux, le problème de la transformation se borne à l'installation d'un unique module, XML::Parser. Pour vérifier la syntaxe et la cohérence du document XML par rapport aux DTD, on peut utiliser le module XML::Checker ou le petit programme rxp. Nous ne détaillerons pas ici l'installation de ces logiciels car elle ne présente pas de difficultés particulières: nous supposerons seulement qu'ils sont installés.


3. Création d'un document XML

3.1. Structure d'un document type

Avant de commencer à écrire notre texte, commençons par penser à sa structure. Pour nous un article comporte:

Ce texte peut être enrichi par des mises en forme particulières:

Il s'agit donc d'une structure relativement simple, mais couvrant la majeure partie des documents classiques. Par ailleurs, cette DTD évolue tout en sachant qu'il n'est pas question de multiplier les balises : "small is beautiful"...

3.2. Écriture d'une DTD pour définir la structure

Cette structure ayant été mise au point, nous pouvons l'implémenter sous la forme d'une DTD: article.dtd

<!ENTITY % titre.com  "titre CDATA #REQUIRED">

<!ENTITY % url.com    "nom CDATA #REQUIRED
                          lien CDATA #REQUIRED">

<!ENTITY % label.com  "id CDATA #REQUIRED">

<!ENTITY % type.com    "type CDATA #IMPLIED">

<!ELEMENT article (tete?,resume?,toc?,corps)>
<!ATTLIST article %titre.com;>

<!ELEMENT tete (version?,date,auteur)>
<!ELEMENT corps (section)+>

<!ELEMENT date  (#PCDATA)>
<!ELEMENT version  (#PCDATA)>
<!ELEMENT auteur  (#PCDATA)>

<!ELEMENT resume (#PCDATA)>

<!ELEMENT toc EMPTY>

<!ELEMENT para (#PCDATA)>

<!ELEMENT section (para|subsection)+ >
<!ATTLIST section %titre.com;>

<!ELEMENT subsection (#PCDATA)>
<!ATTLIST subsection %titre.com;>

<!ELEMENT em (#PCDATA)>
<!ELEMENT tt (#PCDATA)>

<!ELEMENT url (#PCDATA)>
<!ATTLIST url %url.com;>

<!ELEMENT liste (item)+ >
<!ATTLIST liste %type.com;>

<!ELEMENT item (#PCDATA)>

<!ELEMENT verbatim (#PCDATA)>

<!ELEMENT label (#PCDATA)>
<!ATTLIST label %label.com;>

<!ELEMENT ref (#PCDATA)>
<!ATTLIST ref %label.com;>

Attention: cette DTD fera probablement se gausser les spécialistes... Passé ce moment de franche rigolade, qu'il aient la gentillesse de m'expliquer mes erreurs (c'est aussi pour cela que j'écris ce document : pour progresser...).

Avec cette DTD, on peut écrire la version XML du document que vous êtes en train de lire et que je vous invite à consulter afin de noter l'utilisation des balises que j'ai définies.

3.3. Explication rapide de la syntaxe d'une DTD

Vous noterez la simplicité de la syntaxe d'une DTD XML : on définit les balises avec :

<!ELEMENT nom_balise (contenu)>
contenu est ce qui sera contenu entre la balise ouvrante (<nom_balise>) et la balise fermante (</nom_balise>). On peut utiliser le signe | pour indiquer les différentes possibilités, la virgule (,) pour énumérer les possibilités et les métacaractères +, *, ? qui ont leur sens habituel: respectivement « une ou plusieurs fois », « zéro ou plusieurs fois », « zéro ou une fois ». #PCDATA désigne tout ce qui n'est pas une balise. EMPTY indique que cette balise ne contiendra rien (utile pour marquer une action, l'insertion d'une table des matières, par exemple).

Une balise peut avoir des attributs. Ceux-ci sont définis par:

<!ENTITY % groupe_attributs  liste_attributs>
groupe_attributs est le nom par lequel on désignera un ensemble d'attributs, liste_attributs est une suite de chaînes (entre apostrophes doubles). Chacune de ces chaînes est de la forme:
"nom_attribut CDATA présence"
présence indique si l'attribut est obligatoire (#REQUIRED ou facultatif (#IMPLIED). Il existe également #FIXED... CDATA indique que la valeur de l'attribut sera une chaîne de caractères, ID que ce sera un identifiant numérique. Il existe également NMTOKEN dont j'ignore encore la signification...

Lorsqu'un groupe d'attributs a été défini, on l'associe à une balise de la façon suivante:

<!ATTLIST nom_balise %groupe_attributs;>

Notez le point-virgule après le nom du groupe d'attributs...

Enfin, on définit des entités (des symboles spéciaux que l'on utilisera ensuite dans notre texte sous la forme de la façon suivante:

<!ENTITY symbole code>
code est une chaîne de caractères représentant le code du symbole.

Une dernière chose, XML est sensible à la casse, respectez celle que je viens de vous indiquer...

3.4. Validation d'un document XML

Passons maintenant à la validation : dans le jargon XML, un document peut être bien formé, ce qui correspond au cas où les balises sont correctement appariées. Si l'on utilise un analyseur non validant, c'est exactement ce qu'il vérifiera. Notamment, si vous avez utilisé les balises <ssection> et </ssection> au lieu de celles définies dans la DTD, <subsection> et </subsection>, l'analyseur n'y verra que du feu puisque votre document XML est bien formé.

Un analyseur validant, par contre, vérifiera que toutes les balises employées sont définies dans la DTD que vous utilisez.

Pour analyser et valider notre document XML par rapport à la DTD qu'il emploie, nous pouvons utiliser le module XML::Checker, qui fait partie de la bibliothèque libxml-enno-1.02, disponible sur CTAN. La création de l'analyseur validant le plus simple qui soit consiste à écrire le script Perl suivant:

#! /usr/bin/perl -w
# 
# Syntaxe : ./check nomfic
#           
# Analyse et valide un fichier XML
#
use XML::Checker::Parser;
use strict;

local $XML::Checker::FAIL = sub { die };

my $parser = new XML::Checker::Parser(SkipExternalDTD => 1, 
                                      ParseParamEnt => 1);
$parser->parsefile (shift);

Si l'exécution de la commande ne produit pas d'erreur, votre document est valide.

On peut, bien sûr, raffiner tout cela. J'avoue ne pas avoir eu le temps de m'y plonger mais les pages de manuel de XML::Checker et XML::Checker::Parser contiennent pas mal d'informations sur le paramétrage de la méthode Parser.

Notons également l'existence d'un analyseur validant, rxp. Son intérêt est qu'il ne nécessite rien d'autre : on récupère les sources, on compile et on dispose d'un exécutable rxp de 84 Ko que l'on peut invoquer de la façon suivante :

rxp Intro-XML.xml

Cette ligne de commande se chargera de vérifier que votre document et la DTD qu'il utilise sont cohérents. À noter que rxp a détecté des problèmes dans ma DTD qui n'avaient pas été repérés par XML::Checker. Sa page de manuel présente ses différentes options.

3.5. Un mot sur l'encodage

L'encodage par défaut de XML est UTF-8, et c'est aussi l'encodage qu'utilise le module XML::Parser. Le problème est que le texte que je tape utilise l'encodage de ma machine, soit ISO-8859-1... D'autre part, même si je tapais directement de l'UTF-8, le HTML que je souhaite générer devrait être converti en ISO-8859-1 pour être visualisé par tout un chacun. Il faut donc définir une fois pour toute une politique d'encodage en attendant que tout cela soit reconnu partout.

J'ai donc choisi la solution suivante :

C'est fastidieux, mais c'est la seule solution que j'ai trouvée et elle est automatisable via l'utilisation d'un Makefile...

3.6. Transformation en HTML

Étant donné que rien ne permet pour l'instant de visualiser directement du XML, il faut bien passer par un format reconnu. Nous étudierons ici comment transformer le document XML en un document HTML (celui que vous lisez si vous utilisez en ce moment un navigateur web).

Avec Perl, et le module XML::Parser, cette transformation se borne à la création d'un script énonçant les règles de transformation de la balise <X> de notre DTD en la balise <Y> de HTML.

Ainsi, par exemple, pour que le texte compris entre <verbatim> et </verbatim> donne un texte compris entre <blockquote><pre> et </pre></blockquote> en HTML, il suffit de créer deux fonctions de transformations (handlers, dans la terminologie de la documentation de XML::Parser):

sub verbatim {
    print "<blockquote><pre>";
}

sub verbatim_ {
    print "</pre></blockquote>";
}

Le principe est simple : le handler portant le nom de la balise sera appliqué à la rencontre de la balise ouvrante, celui portant le nom de la balise suivi d'un _ sera appliqué à la rencontre de la balise fermante.

Le texte entre les deux balises est pris en compte par un handler spécial, qui se bornera ici à reproduire le texte après quelques transformations (principalement pour transformer les '<' et les '>', etc. en leurs codes HTML).

Ce script s'appelle xml2html et je vous invite à le consulter pour noter sa simplicité.

Il est formé de deux parties : la première crée une instance de l'analyseur et le configure pour qu'il utilise les handlers que l'on définira dans l'espace de noms SubHandlers, on lui précise également que les éléments qui ne sont pas des balises seront gérés par le handler que nous avons choisi d'appeler char. Puis on lance l'analyse du fichier passé en paramètre au script à l'aide de la méthode parsefile.

À part char, tous les handlers sont placés dans leur propre espace de noms, qui définit également trois variables pour mémoriser les types des listes, la numérotation des sections et des sous-sections.

On notera que la majeure partie de ces fonctions se bornent à afficher sur la sortie standard une balise HTML. Mais certaines des balises XML que nous avons définies, article, section, subsection, liste et url utilisent des attributs dont il faudra bien tenir compte lors de la transformation.

En fait, chacun de ces handlers recoivent des paramètres lorsqu'ils sont appelés. Ces paramètres sont, dans l'ordre:

Ainsi, le handler url reçoit la liste d'attribut ($p, $balise, %attribs) (dans @_).

Nous nous débarrassons des deux premiers car ils ne nous intéressent pas ici, et nous passons au traitement du troisième, qui sera considéré comme un hachage dont les clés sont les noms des attributs et les valeurs seront celles qui nous intéressent. Ainsi, $attribs{nom} contient le texte qui s'affichera, tandis que $attribs{lien} contient l'URL vers lequel pointe le lien.

Le traitement des listes suit le même principe : il est un peu plus complexe car l'on doit mémoriser le type de la liste que l'on a ouverte pour pouvoir la fermer correctement. Les listes pouvant être imbriquées, nous avons choisi d'utiliser un tableau, types_listes, que nous gérons comme une pile.

3.7. Transformation en LaTeX

La transformation en LaTeX suit exactement le même principe sauf qu'au lieu de générer des balises HTML, on génère des marqueurs LaTeX. Le script xml2tex, fourni avec les sources de ce document, s'acquitte de cette tâche.

3.8. Transformation en DocBook

Un des intérêts des transformations est de pouvoir passer d'une DTD à l'autre. Cette transformation suit le même principe que les précédentes : à une balise de ma DTD, j'associe une balise DocBook... Le script xml2dbk permet de générer un fichier DocBook pouvant être ensuite traité par jade (voir le fichier Makefile.dbk).

On notera aussi qu'il existe des mécanismes plus élaborés pour réaliser cette conversion. À l'heure qu'il est, je n'ai pas encore eu le temps de m'y pencher. Quand ce sera fait, j'ajouterai une section (toutes les contributions sur le sujet sont les bienvenues).


4. Conclusion temporaire

J'espère vous avoir convaincu de la puissance de ce type de génération de document. De sa portabilité, aussi car il suffit d'une archive contenant les fichiers XML et DTD et les scripts Perl permettant de générer les formats pour que le destinataire puisse produire les formats qu'il désire (moyennant l'installation de XML::Parser).

Vous remarquerez que l'extension de ce procédé à tout format connu consiste, finalement, à écrire les handlers de gestion des balises. Vous pouvez d'ailleurs jeter un coup d'oeil au script xml2tex qui transforme tout document utilisant ma DTD en fichier LaTeX (ces deux scripts sont encore imparfaits, mais j'y travaille...).

Cette extension s'applique à la DTD : l'ajout d'une nouvelle balise passe par sa déclaration dans la DTD et par sa prise en compte dans les scripts.

Les heureux utilisateurs de Emacs et de PSGML seront ravis d'apprendre que celui-ci leur facilitera la saisie de leurs documents XML en prenant en compte leur DTD et en insérant automatiquement les balises et attributs qu'ils ont définis.

Ce document n'est encore qu'un brouillon (cf. son numéro de version). Je compte sur les relectures et les suggestions pour le faire évoluer vers une version débarrassée des imprécisions et erreurs.


5. Références


6. Remerciements

Je remercie tous ceux qui ont répondu aux questions que j'ai posées dans fr.comp.text.xml, notamment Thierry Bezecourt, Jean-François Billaud, Xavier Cazin et Jérôme Mainaud. Merci également à Stéphane Bortzmeyer pour m'avoir convaincu de l'intérêt des DTD pour la production de documents structurés.

Ce document a été soumis à la sagacité de Xavier Cazin et Olivier Tharan.