Académique Documents
Professionnel Documents
Culture Documents
August 6, 2001
2
Contents
3
4 CONTENTS
3 XSLT 109
3.1 Programmes XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
3.1.1 Structure d’un programme XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . 111
3.1.2 Modularité : xsl:import et xsl:include . . . . . . . . . . . . . . . . . . . 115
3.1.3 Application d’un programme XSLT . . . . . . . . . . . . . . . . . . . . . . . . . 117
3.2 Les règles XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
3.2.1 Les patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
3.2.2 Règles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
3.2.3 Déclenchement de règles avec xsl:apply-templates . . . . . . . . . . . . . 124
3.2.4 Sélection des règles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
3.2.5 Appel de règle avec xsl:call-template . . . . . . . . . . . . . . . . . . . . 131
3.2.6 Paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
3.3 Instructions de contrôle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
3.3.1 Tests : xsl:if et xsl:choose . . . . . . . . . . . . . . . . . . . . . . . . . . 134
3.3.2 Boucles : xsl:for-each . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
3.3.3 Variables : xsl:variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
3.3.4 Tri : xsl:sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
3.3.5 Itérations par récursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
3.4 Évaluation d’un programme XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
Ce livre traite de la publication de données, dans un environnement web, à partir de documents XML et
de programmes (ou « feuilles de styles ») XSLT. Le terme de publication employé ici doit être pris dans
un sens très général. Il recouvre toutes les transformations visant à mettre en forme un contenu afin de
le rendre accessible via un ou plusieurs médias de communication. Cette définition très souple s’applique
bien entendu à la publication web prise dans son acception la plus classique : rendre disponible un contenu
au format HTML sur une serveur connecté à l’Internet. Mais elle recouvre également de très nombreux
types de publications que nous aurons l’occasion d’aborder dans ce livre : publication WAP pour téléphones
mobiles, mise en forme pour une impression papier, présentations interactives multimédia, etc.
Il existe plusieurs approches pour la publication de données. Une première solution est de gérer les
données séparément dans un format adapté à chaque type de publication, avec des inconvénients évidents
dus à la duplication des informations. Un exemple très concret de ces limitations est donné par le Web, qui
est encore essentiellement constitué de pages HTML. Si un contenu est placé dans une telle page, il pourra
certes être affiché correctement dans un navigateur, mais il deviendra également impropre à être repris et
exploité dans un autre contexte (document papier, système de base de données, ...). Ce qui est vrai pour
HTML l’est également, à des degrés divers, pour toute autre représentation.
Il est donc très rapidement devenu évident qu’il était souhaitable de dissocier présentation et contenu.
Parmi les très nombreuses raisons justifiant ce principe, on peut citer :
la nécessité de baser la présentation sur une « maquette » définissant une mise en forme applicable à
de nombreuses occurrences de contenu (exemple typique des maquettes de quotidiens appliquées à
un contenu qui change tous les jours) ;
enfin, la lourdeur d’une gestion de sites web statiques : les webmestres doivent en effet être compé-
tents en graphisme, en HTML, en JavaScript, et savoir faire des choix relatifs au système d’information
de leur entreprise.
Une solution maintenant largement adoptée est de s’appuyer sur un serveur de données centralisant
l’information, et d’écrire pour chaque type de publication une application séparée qui utilise des outils
adaptés. Pour reprendre le domaine de la publication web, la plupart des sites s’appuient maintenant sur
un système de gestion de bases de données (SGBD), en extraient des informations avec SQL, et produisent
une page HTML intégrant ces informations avec un langage comme PHP, ASP ou les Java Server Pages
(JSP). La complexité des applications web peut varier du transfert simple de résultats de requêtes SQL
et la réservation de billets d’avion avec des mises à jour sur différentes bases de données. Cette solution
résout les principaux problèmes évoqués ci-dessus, et notamment rétablit une certaine indépendance entre
contenu et présentation. En revanche le simple énoncé des compétences nécessaires montre qu’elles sont
plutôt alourdies : ils faut maintenant non seulement maîtriser les langages propres au web, comme HTML
et JavaScript, mais également disposer de connaissances de graphiste, de programmeur, de concepteur et
administrateur de bases de données.
Un des arguments du présent livre est de montrer que l’utilisation de XML en lieu et place de HTML
permet de mettre en place des mécanismes beaucoup plus puissants, et notamment d’obtenir, à partir d’un
7
8 CONTENTS
même contenu, à peu près n’importe quel format de sortie (HTML, XML, PDF, WML, LaTeX, ...). Un
document XML est beaucoup plus souple, flexible et maniable qu’une base de données dont la mise en
œuvre suppose des compétences techniques assez avancées. De plus la mise en forme de l’information à
partir d’un document XML est grandement facilitée par l’existence d’un langage spécialisé : XSLT.
XSLT
XSLT (eXtensible Stylesheet Language Transformations) est un langage permettant d’extraire et de restruc-
turer des informations présentes dans un document XML. Cette restructuration peut être faite en HTML, si
on vise à produire un site au sens classique du terme, mais un programme XSLT peut également fournir,
pour tout ou partie des informations, une version imprimable en PDF, voire Word/RTF. On peut également,
dynamiquement, adapter la présentation au client qui accède au site, comme l’illustre le cas typique de
deux versions, WML et HTML, d’un site web.
La publication avec XSLT va au-delà de la seule mise en forme destinée à une consultation statique. On
peut utiliser XSLT dans une optique d’échange et d’intégration de données. Avec XML il est possible en
effet de rendre publiques sur le Web, en passant par une transformation XSLT appropriée, des informations
qui peuvent être reprises et traitées par d’autres. On peut envisager, par exemple, un système coopératif
où il n’y a plus besoin, pour créer un site web, de devoir gérer soi-même la base de données, le serveur, la
sécurité, la présentation. Le contenu est fourni à quelqu’un sous forme de documents XML, dans le format
qu’il attend, et ce quelqu’un se charge de l’intégration avec d’autres documents et la mise en forme, HTML
ou autre. Autre application possible de cette capacité d’échanges de données : l’indexation par un moteur
de recherche, dont le rôle est de collecter les informations sur un thème donné, et d’offrir des modes de
recherches sophistiqués pour ces informations.
XML rend possible la représentation de l’information dans un format universel, reconnu sur tous les
systèmes, et adapté à une répartition sur le Web. En utilisant des feuilles de style XSLT on peut extraire,
reformater, et définir la présentation adéquate pour ces informations XML. On résout, en d’autres termes,
un problème très courant : comment éviter de dupliquer des informations pour s’adapter soit à une applica-
tion donnée, soit au type particulier de publication/communication (papier, écran, transparent, email) que
l’on souhaite utiliser. Il devient possible d’imaginer un système d’information véritablement réparti sur un
réseau, dans lequel les nœuds peuvent s’échanger des informations et se spécialiser chacun dans un type
de service particulier.
un langage de script intégré aux pages HTML (PHP ou JSP) car elle permet une séparation des points
de vues et donc des compétences : celui qui intègre les informations en XML, en utilisant des langages
comme Java et SQL, peut être différent de celui qui publie ces informations à destination de différents
médias, en s’appuyant sur des connaissances XSLT et – par exemple – HTML. En incluant dans ce livre
des développements techniques sur une chaîne de publication complexe allant d’une base de données à un
document final au format PDF ou HTML, en passant par une intégration XML, nous pensons proposer un
panorama complet de ce que peut être un environnement de publication web construit sur XSLT.
Afin de donner des illustrations concrètes aux concepts évoqués ci-dessus, une partie importante des
exemples donnés dans ce livre est consacrée à des applications simplifiées, mais réalistes, de la manipula-
tion de documents XML avec XSLT. Nous invitons le lecteur à utiliser nos deux maquettes, disponibles sur
le site du livre, et à récuperer, puis modifier le code de ces exemples.
Les exemples
Sauf indication contraire, tous nos exemples sont conformes aux documents normatifs publiés par le World
Wide Web Consortium et disponibles sur le site http://www.w3c.org. Il devraient donc fonctionner avec
tous les serveurs d’application ou clients web dotés d’un processeur XSLT. Nous utilisons cependant, à
titre d’outil de référence, l’environnement Tomcat/Cocoon proposé par la fondation Apache et librement
disponible sur le site http://xml.apache.org. Nous proposons dans l’annexe A un guide d’installation et
de configuration de ces outils, sous Linux ou Windows, qui vous permettront de tester nos exemples ou
d’effectuer vos propres investigations sans investir dans un produit commercial.
Dans les cas où nous devons faire appel à des fonctionnalités non standardisées, nous avons choisi, par
souci de cohérence, de faire encore appel aux solutions proposées dans Cocoon, en essayant cependant
de mettre en valeur les aspects génériques qui se retrouvent dans la plupart des produits. C’est le cas
notamment pour la création dynamique de documents XML à partir d’une base de données pour laquelle
nos exemples sont basés sur XSP, une technique d’intégration de code java dans du XML que l’on retrouve
dans Cocoon, dans AxKit (www.axkit.org), et qui s’inspire fortement des Java Server Pages.
Vous pouvez récupérer tout le code de nos exemples sur le site du livre, afin de l’utiliser, le consulter
ou le modifier pour vos propres besoins.
http://cortes.cnam.fr:8080/XBOOK
Audience et pré-requis
Ce livre est destiné à tous ceux qui s’interrogent sur les applications liées à XML, et plus particulièrement
sur la validité d’une aproche basée sur XML pour la mise à disposition de données sur le Web. Nous
pensons que ce public recouvre plus spécifiquement les types de lecteur suivants :
1. les architectes d’applications envisageant d’utiliser XML comme format d’échange de données entre
différentes applications où comme support de base de leur système d’information ;
2. les chefs de projet qui doivent intégrer dans la conception de leurs produits un ou plusieurs modules
de communications de données via le Web ;
3. les développeurs d’application à qui ce livre donne les indications nécessaires pour produire une
présentation uniforme de leur site fondée sur l’utilisation de feuilles de style.
4. enfin toute personne cherchant une présentation pratique et complète des nouvelles méthodologies
de publications sur le web.
Nous supposons que le lecteur dispose par ailleurs d’une connaissance minimale des sujets suivants :
1. les bases des applications web, incluant HTML, la gestion de formulaires, quelques notions sur la
programmation CGI et les grandes lignes d’une architecture web ;
10 CONTENTS
Il existe de très nombreux livres qui traitent des aspects ci-dessus. Nous recommandons par exemple
HTML : The Definitive Guide, Java in a Nutshell et (?) tous aux Éditions O’Reilly.
Organisation du livre
Le livre suit une double démarche de présentation des aspects les plus simples, puis, progressivement,
les plus complexes du langage XSLT, et d’illustration des mécanismes de ce langage à des cas concrets
d’utilisation. Nous avons choisi de commencer par un chapitre introductif en forme d’étude de cas qui pro-
pose, sur une application de type « Officiel des spectacles » adaptée au web, une déclinaison des différents
thèmes couverts. La lecture préalable de ce chapitre est probablement indispensable, au moins pour les
néophytes.
Les chapitres suivants traitent de manière plus systématique du couple XML/XSLT en reprenant de
manière approfondie les aspects présentés de manière délibérement intuitive dans le premier chapitre. Le
fil conducteur est cette fois une application publiant sous différentes formes les programmes d’organismes
de formation (universités, instituts techniques comme le Cnam, etc), mais nous utilisons également parfois
des exemples plus abstraits quand il faut privilégier l’explication des mécanismes XSLT.
Dans la quasi-totalité du livre nous essayons d’être indépendant d’un outil ou type d’outil particulier
afin de respecter le caractère normalisé de XML/XSLT. Le dernier chapitre propose cependant une étude de
cas détaillée montrant, pour un type d’environnement particulier, en l’occurrence la suite Apache/Tomcat/Coocon,
la réalisation d’un site web.
Plan détaillé
Une introduction à XML/XSLT. Ce premier chapitre fournit une présentation relativement courte de la
problématique d’ensemble abordée par le livre. Il peut se lire indépendamment du reste du livre, et
propose des arguments et des exemples simples, ainsi qu’une introduction intuitive des mécanismes
qui sont à la base des transformations XSLT.
Les apports de XML/XSLT sont successivement présentés, tous étant, dans les chapitres suivants,
repris et développés avec des descriptions techniques approfondies.
Bases de XSLT. Le cœur du sujet : à partir d’un ou plusieurs documents décrivant des formations, on
explore les principales possibilités de XSLT.
DTD. XSLT peut être vu comme un outil permettant de transformer un document XML représentant un
contenu indépendamment de toute application, vers un autre document XML, spécialisé, et dédié à
un application particulière (par exemple un navigateur web).
Plusieurs spécialisations sont présentées dans ce chapitre :
1. XHTML ;
2. RSS ;
3. WML ;
4. (?)
CONTENTS 11
Conventions
Nous utilisons les conventions typographiques suivantes :
Remerciements
Irini Fundulaki, Cédric Dumouza, Laurent Mignet, Michel Scholl, Luc Ségoufin, Dan Vodislav
12 CONTENTS
Chapter 1
Sommaire
1.1 L’application et ses besoins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.2 XML, format universel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.2.1 Qu’est-ce que XML ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2.2 Structuration avec XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2.3 Documents XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.3 Publication de données avec XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.3.1 Site web (HTML) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.3.2 Site WAP (WML) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
1.3.3 Document papier (PDF) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
1.4 Échange et intégration de données en XML . . . . . . . . . . . . . . . . . . . . . . 42
1.4.1 Exemple : Le Site www.sallesenligne.com . . . . . . . . . . . . . . . . . . . . 42
1.4.2 Description de la structure d’un document XML . . . . . . . . . . . . . . . . . 43
1.4.3 Transformation et échange de données . . . . . . . . . . . . . . . . . . . . . . 44
1.4.4 Un moteur de recherche XML/XSLT . . . . . . . . . . . . . . . . . . . . . . . 45
1.4.5 Intégration dynamique de fragments XML . . . . . . . . . . . . . . . . . . . . 46
1.5 Comment lire la suite de ce livre ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Ce chapitre a pour ambition de proposer au lecteur une présentation intuitive des principales caractéris-
tiques de XML, ainsi qu’un tour d’horizon de ses apports en terme d’intégration, d’échange et de publi-
cation de données. Nous évitons délibérément, à ce stade, toute discussion technique détaillée, l’objectif
étant plutôt de dresser un panorama des contextes d’utilisation de XML et de son association avec XSLT,
en vue de convaincre le lecteur de l’intérêt de recourir à ces langages.
Nous prenons comme fil conducteur, dans ce chapitre, une application simple qui nous permettra de
décliner des exemples d’utilisation de XML. Cette application consiste à fournir, sous les formes les plus
variées, les informations relatives aux films à l’affiche en France, comprenant un descriptif de chaque film,
et les cinémas, salles et séances où ces films sont projetés. Nous supposons de plus que ces informations
sont disponibles en différents points du réseau Internet. Nos exemples montreront comment XML permet
d’échanger ces informations, de les intégrer, et enfin de les publier sur les supports les plus divers.
L’accent est mis bien entendu sur l’association de XML avec le langage de transformation XSLT, mais
nous discutons également, plus brièvement, des interfaces de programmation, ainsi que des rapports entre
XML et les bases de données relationnelles. Nous souhaitons que ce premier chapitre permette, sans inve-
stir dans une étude technique approfondie, de dégager clairement la place de XML au sein des nombreux
outils, langages et techniques qui constituent un système d’information orienté vers la publication ou les
échanges de données sur le Web. Pour tous les aspects qui ne sont, techniquement parlant, qu’esquissés
dans cette introduction, nous indiquons finalement le chapitre ou la partie du livre où le lecteur trouvera un
développement complet.
13
14 CHAPTER 1. INTRODUCTION À XML ET XSLT
Le film Alien, de Ridley Scott, projeté dans la salle 1 avec trois séances dans la journée ;
le film Vertigo, d’Alfred Hitchcock, projeté dans la salle 2 avec une seule séance à 22 heures.
L’Épée de bois souhaite bien entendu rendre ces informations disponibles sur son site web. Mais il
envisage également le cas de cinéphiles munis d’un téléphone mobile, susceptibles de consulter les séances
via une application WAP. Enfin le programme des salles doit être affiché à l’entrée du cinéma, distribué dans
l’environnement proche (cafés, librairies) sous forme de tracts, et transmis à un magazine d’informations
sur les spectacles.
Pour tous ces modes de diffusion, la solution traditionnelle, basée sur des outils adaptés à chaque cas,
implique de resaisir l’information, avec des risques d’erreur multipliés et une perte de temps inutile. Par
exemple la plaquette serait stockée au format propriétaire d’un traitement de mise en page, chaque site web
placerait le contenu dans des fichiers HTML, le magazine conserverait ce même contenu dans une base de
données, et ainsi de suite.
Supposons de plus qu’un site, www.sallesenligne.com, propose de référencer toutes les séances de tous
les cinémas en France, et offre aux internautes un service de recherche et d’indexation. Bien entendu cela
suppose que chaque cinéma lui fournisse, dans un format donné, les informations sur ces propres séances,
ce qui implique pour l’Épée de bois un travail supplémentaire de saisie et mise à jour.
Enfin on supporsera qu’un dossier complet sur chaque film (acteurs, résumé) est disponible dans une
base de données interrogeable sur le web, et que chaque journal tenant une rubrique « cinéma » offre un
accès sur le Web aux critiques parues sur les films. Il serait souhaitable que cette information puisse être
intégrée au programme pour le rendre encore plus attrayant.
En résumé, la problématique est double : d’une part il faut être en mesure de fournir une même infor-
mation – le programme de cinéma – sous les formes les plus variées, d’autre part il faut « récupérer » tout
ce qui peut enrichir cette information, et intégrer le tout dans un format cohérent. Nous allons explorer
dans la suite de ce chapitre comment XML/XSLT répond à ce double besoin.
Ces quelques informations constituent un contenu susceptible d’apparaître sur de nombreux supports
différents : des affiches de film, un magazine des spectacles à Paris, de très nombreux sites web, des
plaquettes commerciales, un téléphone portable, etc. Dans un contexte cloisonné où ces différents supports
sont produits indépendamment les uns des autres, ce contenu est habituellement dupliqué autant de fois
que nécessaire, et associé à un format propre à chaque support.
Dans la mesure où les applications gérant ce contenu ne communiquent pas, cette duplication et cette
hétérogénéité des formats, adaptés à chaque type d’exploitation de ce contenu, sont légitimes. Si, en
revanche, on se place dans un environnement connecté au réseau et favorisant les échanges d’information,
duplication et hétérogénéité deviennent beaucoup moins justifiables. La duplication induit un coût, de
transformation ou de stockage, et l’hétérogénéité peut rendre inaccessible ou inexploitable une information
pourtant présente.
1.2. XML, FORMAT UNIVERSEL 15
Une information codée en XML est donc simplement représentée sous forme d’une chaîne de carac-
tères. Cette chaîne débute obligatoirement par une déclaration XML :
qui indique que la chaîne contient des informations codées avec la version 1.0 de XML, et que le jeu de
caractères utilisé est conforme à la norme ISO-8859 définie par l’Organisation Internationale de Standard-
isation (ISO) pour les langues latines. Cette norme est adaptée à l’usage du français puisqu’elle permet les
lettres accentuées commme le ’é’ dans le mot Epée.
contient la même information que la chaîne XML précédente, mais est difficilement exploitable par un
outil informatique car la structure de la phrase est cachée. Cette structure est marquée, en XML, par des
balises 1 encadrées par les symboles et . Les balises CINEMA , NOM , /NOM , ADRESSE ,
/ADRESSE , METRO , /METRO et /CINEMA décomposent ainsi le contenu en trois parties
textuelles :
Epée de Bois ;
Censier-Daubenton.
On peut constater que les balises forment des parenthèses autour de fragments de la chaîne XML.
On trouve dans notre exemple les « parenthèses » CINEMA . . . /CINEMA , NOM . . . /NOM ,
ADRESSE . . . /ADRESSE , METRO . . . /METRO . Dans ces paires de balises, la première est
appelé balise ouvrante et la deuxième la balise fermante. Voici la terminologie établie pour désigner les
constituants d’une chaîne XML :
une paire de balises ouvrante et fermante et le fragment qu’elles entourent constituent un élément
XML ;
le contenu d’un élément XML est obtenu en enlevant les balises qui l’entourent.
1 XML est un langage de balisage structurel.
16 CHAPTER 1. INTRODUCTION À XML ET XSLT
La structuration avec XML permet d’une part de désigner certaines parties du contenu avec des noms
d’éléments, et d’autre part de structurer ce contenu en définissant une hiérarchie entre les éléments. Une des
principales règles de structuration est en effet que le parenthésage défini par les balises doit être imbriqué :
si une balise B est ouverte entre deux balises A et /A définissant un élément, elle doit également
être fermée par /B entre ces deux balises. Cette contrainte introduit une hiérarchie entre les éléments
définis par les balises. Par exemple, l’élément ADRESSE 100, rue Mouffetard /ADRESSE
est un sous-élément de l’élément défini par la balise CINEMA . Ce dernier englobe tout le document
(sauf la première ligne) et est appelé l’élément racine du document.
On peut noter que le nom des balises, ainsi que l’imbrication de ces balises, sont totalement libres : il
n’existe pas en XML de règles prédéfinies. Cela permet à chacun de définir son propre langage pour décrire
ses données. L’Épée de bois s’est défini un langage basé sur le vocabulaire CINEMA, NOM, ADRESSE et
METRO, et une règle de construction très simple consistant à imbriquer dans CINEMA les trois autres
éléments. Nous parlerons parfois de « dialecte » pour désigner un langage défini avec XML.
La structure hiérarchique d’un contenu XML devient plus explicite si on ajoute des changements de
ligne et des espaces dans le document :
Cette chaîne est considérée comme identique à la première car les caractères blancs et les sauts de ligne
entre deux balises ne sont pas pris en compte par un traitement. L’indentation est souvent utilisée pour clar-
ifier un contenu XML, sans avoir d’impact sur ce contenu lui-même. En fait, ce que cette pratique suggère,
c’est qu’une approche fructueuse pour faciliter la compréhension et le raisonnement sur une information
structurée avec XML est de considérer que cette information est un arbre, et pas une chaîne de caractères.
La figure 1.1 montre l’arbre correspondant au cinéma « L’Epée de bois ». Il a pour racine un élément
CINEMA donc le contenu est lui-même constitué des trois sous-éléments NOM , ADRESSE et
METRO . Chacun de ces trois sous-éléments a un contenu qui est du texte simple.
CINEMA
Cette représentation, plus abstraite, permet de mettre l’accent sur les deux aspects vraiment essentiels
d’un contenu structuré avec des balises XML. En premier lieu elle montre quels sont les noms d’éléments
qui décomposent et désignent les différentes parties du contenu. En second lieu elle permet de situer
précisément la place de chaque élément au sein de la hiérarchie globale. Il est très important de noter dès
maintenant que ces deux aspects sont indissociables : un élément est caractérisé à la fois par son nom
et par sa place dans l’arbre XML. Concrètement, cela implique que tout traitement de données XML –
par exemple en vue de les mettre en forme pour une publication web – se base sur des outils permettant de
1.2. XML, FORMAT UNIVERSEL 17
choisir des éléments par leur nom, ou par leur position, ou par les deux dimensions utilisées simultanément.
Le principal outil utilisé dans ce livre pour sélectionner des éléments XML est le langage XPath, mais nous
donnerons également un aperçu des interfaces de programmation Sax et DOM qui remplissent la même
fonction.
Voici un exemple plus général illustrant la structure d’un document. Il développe la description du
cinéma « L’Epée de bois ». Ce cinéma a deux salles, chacune représentée par un élément XML de type
SALLE . Pour chaque salle on connaît le titre du film projeté et les horaires des séances. La première
salle a également un sous-élément de type REMARQUE qui est absent pour les deux autres salles.
Exemple 3 ExXML1.xml : Un cinéma, avec salles et séances
<?xml version="1.0" encoding="ISO-8859-1"?>
<CINEMA>
<NOM>Epée de bois</NOM>
<ADRESSE>100, rue Mouffetard</ADRESSE>
<METRO>Censier-Daubenton</METRO>
Cet exemple introduit un nouvel aspect de la représentation d’une information en XML : les attributs.
Dans les premiers exemples donnés précédemment, toute l’information concernant un type d’élément était
codée dans le contenu des éléments, ou, autrement dit, dans le texte entouré par les balises de l’élément. Ici
(exemple 3), le numéro et le nombre de places disponibles pour chaque salle ne font pas partie du contenu
de l’élément correspondant, mais sont indiqués au sein des balises même sous forme d’attributs NO et
PLACES. La même information aurait pu être codée avec des sous-éléments.
La figure 1.2 montre l’arbre XML. On peut en tirer quelques constatations générales :
cet arbre n’est pas équilibré (certaines branches sont plus longues que d’autres) ;
certains types d’éléments se répètent (cas des salles et des séances) et d’autres pas (cas du nom du
cinéma) ;
certains types d’éléments apparaîssent régulièrement comme sous-élément d’un autre type d’élément
(cas des salles qui sont des sous-éléments des cinémas et des titres qui sont des sous-éléments des
salles) ;
les éléments de certains types sont optionnels (cas des remarques).
Nous utiliserons abondamment la représentation arborescente pour soutenir nos explications sur les
mécanismes de transformation XSLT. Elle permet de s’affranchir de détails inutiles (comme, par exemple :
d’où vient cette chaîne ? comment est-elle indentée ?) et de travailler sur une représentation claire et
pertinente du contenu et de sa structuration.
18 CHAPTER 1. INTRODUCTION À XML ET XSLT
CINEMA
Réservation conseillée
Cette distinction, ainsi que la différence entre document XML et fichier, sont illustrés par l’exemple
suivant. Imaginons que dans notre application, chaque salle est gérée par un responsable qui doit tenir à
1.2. XML, FORMAT UNIVERSEL 19
jour les informations. Il existe alors autant de fichiers XML qu’il y a de salles. Le document relatif à la
salle 2 est le suivant :
Exemple 4 Salle2.xml : La salle 2, représentée indépendamment
<?xml version="1.0" encoding="ISO-8859-1"?>
On retrouve les attributs et les éléments SEANCE , et l’élément FILM auquel on a ajouté des
informations complémentaires : année de production, metteur en scène, résumé, etc. Le contenu de
ce fichier correspond à un document représenté dans la figure 1.3, avec la racine du document notée
Salle2.xml:/, puis l’élément racine SALLE.
Salle2.xml:/
SALLE
<- - Salle 2 du cinéma Epée de Bois - -> NO=’2’ PLACES=’120’
FILM SEANCES
À partir de l’ensemble des documents décrivant les salles, il est possible de reconstituer un document
global en assemblant dans un fichier les informations propres au cinéma, et en important les documents
relatifs aux salles. On utilise la notion XML d’entité externe qui permet de faire référence à une source
de données externe au fichier XML « principal » (appelé entité document), et d’inclure le contenu de cette
source.
Voici une nouvelle représentation du document XML pour le cinéma « L’Epée de bois » (figure 1.2). Il
s’agit bien du même document (avec un description plus détaillée des films), mais réparti dans trois fichiers
ou entités externes :
<!DOCTYPE CINEMA [
<!ENTITY salle1 SYSTEM "Salle1.xml">
<!ENTITY salle2 SYSTEM "Salle2.xml">
]>
<CINEMA>
<NOM>Epée de bois</NOM>
<ADRESSE>100, rue Mouffetard</ADRESSE>
<METRO>Censier-Daubenton</METRO>
L’entité document Epee.xml contient dans son entête deux définitions d’entité externes vers les fichiers
Salle1.xml et Salle2.xml :
Les références vers les entité externes dans l’entité document sont représentées par le nom de l’entité
correspondante entouré des symboles & et ; :
Dans la représentation arborescente du document XML (figure 1.2), toutes les références vers ces en-
tités seront remplacées par le contenu des entités Salle1.xml et Salle2.xml.
Nous serons amené dans la suite de ce livre à généraliser considérablement ce premier exemple où
un document XML est obtenu par assemblage de plusieurs sources. Ici nous sommes dans un cas simple
où les sources sont d’autres fichiers, mais il est possible d’intégrer des composants très divers, incluant
des parties « statiques » (par exemple un ou plusieurs fichiers, éventuellement répartis sur l’Internet) et
des parties « dynamiques », fragments créés à la volée, par exemple par extraction à partir d’une base de
données.
1.3. PUBLICATION DE DONNÉES AVEC XSLT 21
le traitement des données consiste à les mettre sous forme de document XML obéissant à une struc-
ture arborescente donnée (par exemple la structure cinéma-salle-film illustrée dans ce qui précède) ;
la publication des données consiste à extraire un contenu d’un document XML et à le mettre dans for-
mat reconnu par une application de publication particulière (par exemple au format HTML reconnu
par les navigateurs web).
Dans un tel environnement les responsabilités sont clairement partagées. On y distingue ceux qui gèrent
les contenus et doivent établir les documents XML, éventuellement à partir de plusieurs sources de données
– nous reviendrons sur cet aspect ultérieurement – de ceux qui doivent créer une mise en forme pour tel ou
tel support. Nous prenons comme exemple les documents XML de la section précédente, sans avoir besoin
de faire d’hypothèses sur l’origine de ces documents. Les transformations XSLT vont permettre d’obtenir
trois versions différentes de ce même contenu :
un (petit) site web, affichant les informations sur le cinéma, ses salles et ses séances ;
un site WAP permettant de consulter sur un téléphone mobile les mêmes informations ;
enfin un document PDF donnant l’ébauche de ce qui pourrait être un « Officiel des spectacles »
imprimé.
Il s’agit, une nouvelle fois, d’un arbre dont la racine est l’élément html , avec deux fils : head
et body . Le reste du document est du texte libre, encadré par des balises de mise en forme. Sans entrer
pour l’instant dans le détail, on peut :
Ce document HTML peut tout à fait être vu comme un document XML, avec une structure imposée
(notamment pour la racine de l’arbre et ses deux fils), et des noms de balise fixées par la norme HTML.
En fait HTML s’est développé indépendamment de XML et s’avère beaucoup moins rigoureux. Les navi-
gateurs acceptent par exemple des documents sans la racine html , et dans de nombreux cas la balise
fermante d’éléments vides est considérée comme optionnelle. Une norme récente, XHTML, reconsidère la
définition de HTML comme un « dialecte » XML.
La figure 1.4 montre l’affichage de ce document dans le navigateur Netscape. On peut voir que le titre
du film apparaît en caractères gras italiques, le pays d’origine en caractères italiques et le nom du réalisateur
en caractères gras. De plus le texte entoure une image de l’affiche (version française) du film.
Transformations XSLT
Nous allons utiliser une feuille de style XSLT pour produire automatiquement le document HTML à partir
d’un document XML, appelé document source dans ce qui suit. Comme c’est la première fois que nous
1.3. PUBLICATION DE DONNÉES AVEC XSLT 23
rencontrons ce langage, nous commençons par une brève introduction permettant d’en comprendre les
principes de base.
L’application d’une feuille de style XSLT consiste à parcourir les nœuds du document XML, et à
appliquer des règles de transformation à ces nœuds. La notion de règle (nommées template) est centrale
dans XSLT, et la bonne compréhension des aspects suivants est essentielle.
une règle s’applique toujours dans le contexte de l’un des nœuds du document source ;
l’application de la règle consiste à produire un fragment de document, qui peut combiner du texte et
des données extraites du document XML.
Voici un premier exemple de règle de transformation XSLT. Elle consiste à produire une phrase simple
chaque fois qu’un élément FILM est rencontré dans le document source.
<xsl:template match="FILM">
Ceci est le texte produit par application de cette règle.
</xsl:template>
On constate qu’une règle est un élément XML défini par la balise xsl:template. L’attribut match="FILM"
est le motif de sélection de cette balise et indique à quel(s) type(s) d’éléments du document XML traité
s’applique la règle. La règle ci-dessus s’appliquera donc toujours dans le contexte d’un élément de type
FILM. Enfin le contenu de l’élément xsl:template est le corps de la règle et définit le texte produit à
chaque fois que la règle s’applique.
Un programme XSLT est en général constitué de plusieurs règles dont chacune peut s’appliquer à
différents parties d’un document XML. Chaque règle produit un sous-arbre spécifique quand elle rencontre
un élément déclencheur, et l’assemblage de ces sous-arbres constitue le résultat de la transformation. Dans
le cas le plus simple, on spécifie des règles différentes pour chaque type d’élément d’un document, comme
nous l’avons fait ci-dessus pour l’élément FILM du document XML à transformer. Parfois la distinction
par le type de l’élément peut être insuffisante et/ou trop imprécise pour choisir les règles et des motifs de
sélection plus complexes, que nous présenterons plus tard, s’avèrent nécessaires.
Le corps de la règle indique le texte produit quand un nœud du bon type est rencontré dans le document
XML. Dans notre premier exemple, le texte produit est toujours
Ceci est le texte produit par application de cette règle.
et ce quel que soit le contenu de l’élément FILM . En pratique ce genre de règle n’est évidemment pas
très intéressante, et on rencontre plus souvent des règles où le texte reprend et réorganise des parties du
document XML traité. Rappelons que la règle s’applique dans le contexte d’un nœud : le principe consiste
alors à sélectionner, à partir de ce nœud, les parties du document qui vont être produites par la règle.
Prenons l’exemple du document XML représentant le film Vertigo. Quand notre règle s’exécute, le
nœud contexte est l’élément racine de l’arbre. On a accès, depuis cette racine, à tous les fils du nœud
FILM , soit les éléments TITRE , AUTEUR , ANNEE , etc., mais aussi à d’autres nœuds
comme par exemple les nœuds descendants (les fils des fils, etc...) ou les nœuds parents et ancêtres.
Pour sélectionner le contenu d’un élément, on utilise l’élément XSLT xsl:value-of en indiquant
simplement le chemin d’accès à partir du nœud courant. Par exemple le titre du film est obtenu par :
<xsl:value-of select="TITRE"/>
Le fait de préfixer par xsl toutes les balises relevant du langage XSLT permet au processeur de dis-
tinguer les éléments qui doivent être simplement inclus dans le résultat, des éléments qui correspondent à
des instructions XSLT.
La figure 1.5 montre l’interprétation de xsl:value-of, avec ses deux paramètres déterminants :
le contexte d’exécution, qui est ici l’élément FILM , et le chemin d’accès, en l’occurrence l’élément
TITRE , fils de l’élément-contexte. Il s’agit ici de l’exemple le plus simple – mais aussi le plus courant
– où on accède aux descendants directs d’un élément, mais nous verrons que le mode de désignation des
éléments à partir d’un nœud contexte est beaucoup plus général.
24 CHAPTER 1. INTRODUCTION À XML ET XSLT
Contexte d’application de la règle XSLT
select="TITRE" FILM
Nous sommes maintenant en mesure d’enrichir notre première règle pour produire un premier docu-
ment HTML sommaire, mais complet.
<xsl:template match="FILM">
<html>
<head>
<title>Film:
<xsl:value-of select="TITRE"/>
</title>
</head>
<body>
Ici je peux mettre d’autres informations sur le film
</body>
</html>
</xsl:template>
<html>
<head>
<title>Film: Vertigo
</title>
</head>
<body>
Ici je peux mettre d’autres informations sur le film
</body>
</html>
Bien entendu on peut appliquer la même règle à tout document XML ayant la même structure que
Vertigo.xml.
<FILM>
<TITRE>Vertigo</TITRE>
<AUTEUR>Alfred Hitchcock</AUTEUR>
<ANNEE>1958</ANNEE>
<GENRE>Drame</GENRE>
<PAYS>Etats Unis</PAYS>
<RESUME>Scottie Ferguson, ancien inspecteur de police, est sujet
au vertige depuis qu’il a vu mourir son collègue. Elster, son
ami, le charge de surveiller sa femme, Madeleine, ayant des
tendances suicidaires. Amoureux de la jeune femme Scottie ne
remarque pas le piège qui se trame autour de lui et dont il va
être la victime...
</RESUME>
</FILM>
De manière générale le résultat est obtenu en insérant dans des balises HTML des extraits du document
XML. Tous ces extraits sont facilement accessibles à partir de la racine FILM , et nous allons donc
pouvoir nous contenter d’une seule règle qui va « piocher », à partir de la racine, les différents constituants
décrivant le film, et produire les fragments HTML appropriés.
La figure 1.6 illustre ce principe par deux arbres superposés : l’arbre supérieur correspond au document
XML qui doit être transformé en HTML; l’arbre inférieur définit la structure de la page HTML à générer
(pour une meilleure représentation, la racine de l’arbre est en-bas). Tous les nœuds sauf sept feuilles sont
étiquetés par des balises HTML qui définissent la structure du document produit. Les autres noeuds sont de
type xsl:value-of et définissent, pour leur part, la partie dynamique du contenu, obtenue par extraction
de certains éléments du document XML. On peut noter que l’élément TITRE est référencé trois fois et
que l’ordre des instructions de substitution ne correspond pas à l’ordre des éléments insérés dans le résultat.
La règle de transformation, insérée dans une feuille de style complète, crée un document HTML pour
chaque film, autrement dit pour chaque document XML ayant la même structure que Vertigo.xml. Son
corps correspond à l’arbre inférieur dans la Figure 1.6.
<xsl:template match="FILM">
<html>
<head>
<!-- Titre du film -->
<title>Film: <xsl:value-of select="TITRE"/>
</title>
</head>
<body bgcolor="white">
<p>
<!-- Insérer une image GIF avec le titre comme nom -->
<img SRC="{TITRE}.gif" align="left" height="220"/>
<h1>
26 CHAPTER 1. INTRODUCTION À XML ET XSLT
FILM
sele
ct="
AUT
EUR"
se
Vertigo Hitchcock 1958 Drame USA Scotty... lec
t=
"R
ES
UM
E"
selec
sel
select="PA
t="T
ect
select="
select="TITRE
="A
xsl:value-of
ITR
NN
E"
GENRE
YS"
EE
select="TIT
"
Résumé: xsl:value-of
"
"
xsl:value-of
RE"
i xsl:value-of Mise . . . b h3
title xsl:value-of p p
head body
html
xsl:template
match="FILM"
<i><xsl:value-of select="TITRE"/></i>
</h1>
Un programme XSL comme celui présenté ci-dessus est un document XML interprété par un processeur
XSLT : on peut vérifier que c’est un arbre constitué d’une imbrication de balises ouvrantes et fermantes. Il
doit obéir à certaines règles de structuration et de nommage des balises. En particulier toutes les balises
propres à XSLT doivent être préfixées par xsl: avec que le processeur puisse les distinguer des balises à
insérer dans le résultat. Ce préfixe définit un espace de nom qui caractérise les balises XSLT.
L’élément racine du document XSLT Film.xsl est de type xsl:stylesheet et contient deux sous-
éléments de type xsl:output et xsl:template. L’élément xsl:output informe le processeur
XSLT que le document généré est un document HTML conforme à la version 4.0 et utilisant le codage
“iso-8859-1”.
Chemins complexes
Nous prenons maintenant l’exemple de la production du document HTML décrivant une salle de cinéma
pour illustrer la capacité de XSLT à spécifier des chemins d’accès complexes pour extraire des informations
d’un document XML.
Dans le document HTML montrant un film, tous les nœuds TITRE , AUTEUR et autres auxquels
on accédait était les fils (au sens de : descendants directs) de l’élément FILM tenant lieu de contexte
de la règle. Le cas d’un document décrivant une salle de cinéma présente une structure plus riche. La
figure 1.7 montre la salle 1 de notre cinéma. Si on prend comme contexte d’une règle de transformation le
nœud SALLE , on constate que :
certaines informations (le numéro de la salle, le nombre de places) sont représentées comme des
attributs ;
le nœud a trois fils : FILM , REMARQUE et SEANCES ;
les informations décrivant le film sont des petits-fils de SALLE : TITRE et AUTEUR en
sont séparés par FILM ;
l’élément SEANCE est petit-fils de SALLE , et peut de plus être répété un nombre quelconque
de fois ;
enfin l’élément REMARQUE est optionnel : il apparaît pour la salle 1, mais pas pour la salle 2
(voir figure 1.3, page 19).
Ces nouvelles particularités rendent plus complexe l’accès aux informations permettant de créer un
document HTML à partir d’un nœud de type SALLE. Gardons pour l’instant le principe de n’utiliser qu’une
28 CHAPTER 1. INTRODUCTION À XML ET XSLT
FILM
chemin
"REMARQUE"
seule règle faisant appel à des xsl:value-of . Les parties du document Salle1.xml sont alors désignées
par des chemins qui prennent tous leur origine du nœud Salle . Voici quelques exemples, illustrés
dans la figure 1.7.
l’attribut d’un élément est désigné par la concaténation du symbole ’@’ et du nom de l’attribut ; donc
le numéro de la salle est désigné par select="@NO" ;
le descendant d’un élément est désigné en donnant les nœuds successifs rencontrés à chaque niveau
de la branche menant à l’élément ; donc le titre du film est désigné par select="FILM/TITRE" ;
pour parcourir toutes les occurrences d’un même type d’élément, on peut effectuer une boucle
xsl:for-each sur l’ensemble des séances désignées par select="SEANCES/SEANCE" ;
enfin l’élément contexte lui-même est désigné par le symbole ’.’, comme dans select=".".
Voici la règle produisant une présentation HTML et récupérant les informations en exprimant les
chemins présentés ci-dessus.
<xsl:template match="SALLE">
<!-- Extraction d’attribut : no de salle et places -->
<h2>Salle No <xsl:value-of select="@NO"/></h2>
<h3>Capacité: <xsl:value-of select="@PLACES"/> places</h3>
<!-- Description du film: tous les chemins commencent par FILM/ -->
<h3>Film: <a href="{FILM/TITRE}XML.html">
<b> <xsl:value-of select="FILM/TITRE"/>
</b></a></h3>
de <b> <xsl:value-of select="FILM/AUTEUR"/>
</b><br/><i> <xsl:value-of select="FILM/PAYS"/></i>,
<xsl:value-of select="FILM/ANNEE"/>
Appel de règle
Il reste à ajouter les balises html , head et body pour obtenir un document HTML complet.
Jusqu’à présent nous nous sommes volontairement limités à des exemples de productions basés sur une
seule règle. En pratique on est le plus souvent amené à définir plusieurs règles dans un programme XSLT,
et à déclencher des appels de règles à partir d’autres règles.
La règle initiale s’applique en général à la racine du document XML, désignée par ’/’. Elle génère un
document HTML complet avec un élément racine html et deux sous-éléments head et title .
Cette règle est la première règle appliquée par le processeur XSLT pendant la transformation d’un docu-
ment XML.
<xsl:template match="/">
<html>
<head><title>Salle: <xsl:value-of select="SALLE/@NO"/></title></head>
<body bgcolor="white">
<xsl:apply-templates select="SALLE"/>
</body>
</html>
</xsl:template>
L’élément body ne contient qu’un seul sous-élément qui est une instruction XSLT de type xsl:apply-
templates. Le processeur XSLT remplace cet élément par le résultat de la transformation qu’il dé-
clenche. Autrement dit on va appliquer aux éléments SALLE fils de la racine ’/’ du document la règle
spécifique à ce type d’élément, et insérer entre les balises body et /body le fragment HTML
produit.
L’intérprétation du mécanisme d’appel de règles est donné dans la figure 1.8. La partie supérieure
montre l’arbre XML, et la partie inférieure les deux règles du programme XSLT.
La première règle (en bas à gauche) s’applique à l’élément racine, ’/’. Elle produit successivement
un élément html , racine de l’arbre résultat, puis deux fils, head et body .
Au sein de l’élément body se trouve une instruction d’application d’une règle aux éléments fils
de l’élément courant qui sont de type SALLE. Le processeur XSLT cherche donc si un tel élément
existe : c’est le cas et la deuxième règle (en bas à droite) est donc déclenchée. Elle produit des
fragments HTML qui viennent s’insérer dans l’élément body .
La figure 1.9 montre le résultat obtenu alors, tel qu’il s’affiche avec Netscape.
Réutilisation de règles
Passons maintenant à un dernier stade pour compléter cette introduction à XSLT. Nous voulons produire la
page HTML récapitulant tout le programme proposé par L’Épée de bois. La figure 1.10 montre le résultat
obtenu3. Il apparaît clairement que la présentation de chaque salle est identique à celle qui était obtenue
3 Il va sans dire que nous privilégions ici la simplicité du code, au détriment de la qualité de présentation.
30 CHAPTER 1. INTRODUCTION À XML ET XSLT
NO="2"
PLACES="120"
SALLE
Transfroamtion du document (racine)
FILM SEANCES
Transfromation de <SALLE>
TITRE AUTEUR ANNEE GENRE PAYS RESUME SEANCE
select="SALLE"
... xsl:apply-templates
"Salle No" xsl:value-of "Capacité:" xsl:value-of "places"
head body
html h2 h3
...
La seconde règle d’applique dans le contexte d’un élément CINEMA . Elle extrait des informa-
tions relatives au cinéma, et propage la production de code HTML par un nouvel appel à xsl:apply-
templates.
<xsl:template match="CINEMA">
<h1><i> <xsl:value-of select="NOM"/> </i>
</h1><hr/> <xsl:value-of select="ADRESSE"/>,
<i>Métro: </i> <xsl:value-of select="METRO"/>
<hr/>
1.3. PUBLICATION DE DONNÉES AVEC XSLT 31
<xsl:apply-templates select="SALLE"/>
</xsl:template>
Ce dernier appel déclenche la règle relative aux salles, pour tous les éléments SALLE qui sont fils
de l’élement CINEMA . Nous ne redonnons pas cette dernière règle.
L’application de chaque règle engendre un fragment d’arbre HTML, et tout appel xsl:apply-
templates insère dans ce fragment un ou plusieurs sous-arbres. La figure 1.11 montre (partiellement)
l’arbre final HTML obtenu, en distinguant les parties issues de chacune des trois règles impliquées dans la
transformation.
Un programme XSLT peut donc être vu comme l’application successive d’un ensemble de règles,
chacune concourant à la création du résultat final par « assemblage » de fragments qui viennent s’associer
pour former une hiérarchie. Dans la copie d’écran du résultat de la transformation (figure 1.10) on peut
clairement distinguer la partie créée par la règle de transformation pour les salles, reproduisant un motif de
présentation identique pour chaque élément SALLE rencontré.
Un document WML est un arbre avec pour racine un élément wml (de même qu’un document
HTML est un arbre avec pour racine l’élément html ), mais il est divisé en sous-éléments, les « cartes »,
définisant l’unité d’affichage sur l’écran du mobile. Les cartes d’un même document peuvent être liées entre
elles par des liens, ce qui permet à l’utilisateur de naviguer grâce aux boutons de son téléphone.
Un exemple de document WML comprenant une seule carte est donné ci-dessous, avec les informations
sur le film Alien. Toutes les balises WML sont en minuscules, et toutes sont très concises afin de limiter le
coût des transferts de documents.
Une carte est organisée en paragraphes grâce à la balise p , avec au moins un paragraphe par carte, ce
qui est le cas dans l’exemple ci-dessus. Quelques autres balises, reprises de HTML, peuvent être utilisées
1.3. PUBLICATION DE DONNÉES AVEC XSLT 33
html
head body
title h1 hr adresse p p
Règle "/" i
h2 h3 h2 h3
Epée de bois
Salle No 1 Film: Alien Salle No 2 Film : Vertigo
Règle "CINEMA"
Règle "SALLE"
pour définir une mise en page (limitée) du texte sur l’écran du mobile : la balise b affiche le contenu de
l’élément en gras, et la balise br permet de passer à la ligne. La figure 1.12 montre l’affichage obtenu
dans un téléphone Nokia 6150.
Voyons maintenant comment obtenir cette représentation WML à partir du document Alien.xml, donné
ci-dessous.
<FILM>
<TITRE>Alien</TITRE>
<AUTEUR>Ridley Scott</AUTEUR>
<ANNEE>1979</ANNEE>
<GENRE>Science-fiction</GENRE>
<PAYS>Etats Unis</PAYS>
<RESUME>Près d’un vaisseau spatial échoué sur une lointaine
planète, des Terriens en mission découvrent de bien étranges
"oeufs". Ils en ramènent un à bord, ignorant qu’ils viennent
d’introduire parmi eux un huitième passager particulièrement
féroce et meurtrier.
</RESUME>
</FILM>
Nous allons simplement utiliser deux règles. Une première règle, d’initialisation, crée le « squelette »
du document résultat, soit l’équivalent pour WML de la structure html , head , body que nous
avons utilisée pour HTML. Cela consiste :
<xsl:template match="/">
<wml>
<xsl:apply-templates/>
</wml>
</xsl:template>
La seconde règle s’applique à un élément de type FILM . Cette règle produit une carte WML, et
place dans cette carte un extrait des éléments constituant la description du film, à savoir le titre, l’année,
l’auteur et le résumé. Notons que l’ordre dans lequel on utilise ces éléments peut être totalement reconsid-
éré par rapport à celui du document XML initial.
<xsl:template match="FILM">
<card>
<p>
<b> <xsl:value-of select="TITRE"/></b>,
<xsl:value-of select="ANNEE"/>,
<xsl:value-of select="AUTEUR"/><br/>
<xsl:value-of select="RESUME"/><br/>
</p>
</card>
</xsl:template>
Voici finalement la feuille de style complète. Il va sans dire qu’elle s’applique à tout document XML
dont la structure est identifique à celle de Alien.xml, par exemple Vertigo.xml (voir page 24).
<xsl:output encoding="ISO-8859-1"/>
<xsl:template match="/">
<xsl:processing-instruction name="cocoon-format">
type="text/wml"
</xsl:processing-instruction>
<wml>
<xsl:apply-templates/>
</wml>
</xsl:template>
<xsl:template match="FILM">
<card>
<p>
<b> <xsl:value-of select="TITRE"/></b>,
<xsl:value-of select="ANNEE"/>,
<xsl:value-of select="AUTEUR"/><br/>
<xsl:value-of select="RESUME"/><br/>
</p>
</card>
</xsl:template>
</xsl:stylesheet>
L’application de cette feuille de style permet d’obtenir le document de l’exemple 9, et donc l’affichage
de la figure 1.12.
En se positionnant sur le texte tenant lieu de lien, l’utilisateur peut afficher la carte correspondante.
Les flèches en tiretés sur la figure indiquent la navigation entre les cartes : de l’index (affichage dans la
figure 1.14, à gauche), on peut se rendre sur une salle (affichage dans la figure 1.14, au centre), puis d’une
salle sur le film projeté dans la salle (figure 1.14, à droite).
Voici maintenant quelques détails sur la feuille XSLT permettant de produire le document WML. La
règle d’initialisation procède comme précédemment, en produisant un élément wml , puis elle déclenche
trois autres règles, correspondant respectivement aux trois types de cartes : liste des salles, liste des séances,
affichage du film. Voici cette première règle.
<xsl:template match="/">
36 CHAPTER 1. INTRODUCTION À XML ET XSLT
wml
p p p p p
href=Alien href=Vertigo
href=S1
a a a
href=S2
a
<wml>
<!-- création de la carte d’accueil -->
<xsl:apply-templates select="CINEMA"/>
Rappelons comment on désigne le chemin d’accès aux éléments auxquels on applique des règles. Le
principe tient en deux points :
Ici la position courante (autrement dit à laquelle la règle courante s’applique) est la racine du docu-
ment, dénotée « / ». On veut traiter trois types d’éléments, CINEMA , pour produire la carte d’index,
SALLE , pour produire la carte donnant les séances, et FILM pour produire la carte donnant la
description de chaque film.
Étant donnée la position courante située à la racine, et la position de ces éléments dans l’arbre XML, la
désignation de ces éléments se fait de trois manières différentes :
1. l’élément CINEMA est un fils de la position courante : on le désigne simplement par CINEMA ;
2. les éléments de type SALLE sont petit-fils de la position courante, avec comme niveau intermédiaire
CINEMA : on les désigne par CINEMA/SALLE qui indique le chemin à parcourir ;
3. enfin les éléments de type FILM sont arrière-petit-fils de la position courante ; on pourrait les
désigner par CINEMA/SALLE/FILM, mais on utilise ici un moyen plus général : .//FILM désigne
tous les éléments descendant de la position courante, quel que soit leur niveau, nommés FILM .
Un part importante de la programmation XSLT consiste à savoir désigner, à partir d’un nœud courant
dans l’arbre XML, les éléments auxquels on veut appliquer des règles de transformation. Les quelques
exemples donnés ci-dessus correspondent à des cas restreint, et font notamment l’hypothèse que le chemin
1.3. PUBLICATION DE DONNÉES AVEC XSLT 37
d’accès à un élément se fait en descendant dans l’arbre. Nous verrons que le langage est beaucoup plus
général.
Il reste à créer une règle pour chacun des types de carte. Voici la règle, déclenchée sur les éléments de
type CINEMA, qui produit la carte affichant la liste des salles.
<xsl:template match="CINEMA">
<card id="index" title="Programme">
<p align="center">
<xsl:value-of select="NOM"/>
</p>
<xsl:for-each select="SALLE">
<p> <a href="#S{@NO}">
Salle <xsl:value-of select="@NO"/>:
</a>
<xsl:value-of select="FILM/TITRE"/>
</p>
</xsl:for-each>
</card>
</xsl:template>
Rappelons que tout ce qui ne commence pas par xsl:, indiquant les types d’éléments propres à XSLT,
est considéré comme du texte et inclus dans le résultat. Pour un cinéma, on va afficher avec une boucle
xsl:for-each un paragraphe (élement de type p) pour chaque salle. Ce paragraphe contient l’intitulé
de la salle, et sert également d’ancre (élément a ) vers la carte détaillant les séances de la salle. La carte
d’une salle est référencée par #S où est le numéro de la salle, présent dans l’attribut NO. Avec XSLT,
on crée cet identifiant avec l’expression #S{@NO}.
38 CHAPTER 1. INTRODUCTION À XML ET XSLT
Voici maintenant la règle servant à produire les cartes des salles. Notez que chaque carte comprend un
attribut id ayant pour valeur S où est le numéro de la salle. On insère une ancre vers la carte détaillant
le film diffusé dans la salle.
<xsl:template match="SALLE">
<card id="S{@NO}">
<p>Séances salle <xsl:value-of select="@NO"/></p>
<p>
<a href="#{FILM/TITRE}">
Film : <xsl:value-of select="FILM/TITRE"/>
</a>
</p>
<xsl:apply-templates select="SEANCES"/>
</card>
</xsl:template>
Enfin la règle produisant la carte des films est identique à celle que nous avons déjà étudiée (exem-
ple 11).
Ce document débute par quelques éléments qui indiquent les propriétés globales de la mise en page
souhaitée. Sans entrer dans le détail de chaque balise utilisée, on trouve :
! l’élément " fo:simple-page-master # qui définit la dimension (page-height="29.7cm",
page-width="21cm") des pages ;
! l’élément " fo:region-body # qui fixe l’espace entre la « région » du contenu et les bords de
chaque page (margin-top="2cm", margin-bottom="2.5cm", . . . ) ;
! l’élément " fo:page-sequence-master # qui est utilisé pour définir la structure du document
(par exemple : une table des matières, des chapitres, un index, etc) ;
! enfin l’élément " fo:block # qui défini des paragraphes avec l’espace de séparation (space-
before=”20pt” et la taille des caractères (font-size=”10pt”).
40 CHAPTER 1. INTRODUCTION À XML ET XSLT
Les éléments qui suivent correspondent au contenu du document. Ce dernier contient deux paragraphes
(éléments de type fo:block) dont le deuxième est séparé du premier par un espace de 20 points (space-
before="20pt"). La taille des caractères est aussi 20 points par défaut (font-size="20pt" dans
" fo:flow # ) sauf pour le deuxième paragraphe, où la taille des caractères est limité à 10 points.
Le document obtenu peut maintenant être transmis à un processeur XSL qui va se charger de produire
une réalisation concrète. Nous utilisons le processeur FOP qui produit des documents PDF : il va nous
permettre d’engendrer une version imprimable du programme de L’Épée de bois.
<xsl:template match="CINEMA">
<fo:block text-align="center">
<fo:block font-size="40pt" font-weight="bold" space-after="20pt">
<!-- Sélection du nom du cinema -->
<xsl:value-of select="NOM"/>
</fo:block>
<!-- Sélection de l’adresse du cinema -->
<xsl:value-of select="ADRESSE"/>
<!-- Sélection du métro près du cinema -->
(<xsl:value-of select="METRO"/>)
</fo:block>
<fo:block space-before="20pt">
<!-- Transformer chaque salle -->
<xsl:apply-templates select="SALLE"/>
</fo:block>
</xsl:template>
Le nom du cinéma et son adresse sont des sous-blocs d’un bloc spécifiant que le texte sera centré
(attribut text-align="center"). Le nom du cinéma est écrit en caractères gras de 40 points, suivi
de l’adresse et de la station de métro. Pour afficher les séances, la règle suivante est appelée (élément
" xsl:apply-templates select="SALLE"/ # ) :
<xsl:template match="SALLE">
<fo:block text-align="center" space-before="40pt">
<fo:inline font-weight="bold" font-size="26pt">
<!-- Sélection du titre du film -->
<xsl:value-of select="FILM/TITRE"/>
</fo:inline> de
<fo:inline font-style="italic">
<!-- Sélection de l’auteur du film -->
<xsl:value-of select="FILM/AUTEUR"/>
</fo:inline>
<fo:block space-before="5pt">
<!-- Sélection de l’année et du pays du film -->
(<xsl:value-of select="FILM/PAYS"/>,
<xsl:value-of select="FILM/ANNEE"/>)
</fo:block>
</fo:block>
<fo:block space-before="10pt">
<!-- Sélection du résumé du film -->
<xsl:value-of select="FILM/RESUME"/>
</fo:block>
<fo:block space-before="10pt">
<fo:inline font-weight="bold">
<!-- Sélection du numéro de salle -->
1.3. PUBLICATION DE DONNÉES AVEC XSLT 41
20pt
29.7cm
2.5cm
2.5cm
2.5cm
21cm
réponse
www.cine-marseille.fr requête
www.sallesenligne.fr
www.epée-de-bois.fr
Le moteur de recherche sur les séances de cinéma se base sur le titre du film, le début de la séance
et la ville. L’utilisateur remplit un formulaire avec les critères désirés et obtient une liste des séances
correspondantes avec des liens vers les sites locaux (Figure 1.18).
Le site www.sallesenligne.com offre donc une valeur ajoutée aux informations déjà existantes au niveau
de chaque site élémentaire. En contrepartie, il demande que les informations nécessaires à la recherche lui
soient fournies sous un format XML imposé dont voici un exemple :
<FILM>
<TITRE>Vertigo</TITRE>
<CINEMA>Epée de Bois</CINEMA>
<VILLE>Paris</VILLE>
<URL>www.epée-de-bois.fr/ExCinema1.xml</URL>
<HEURE>22:00</HEURE>
</FILM>
À part le titre, le cinéma, la ville et les heures de projection, l’élément " FILM # contient un sous-
élément de type URL avec l’adresse de la page web du cinéma. Ces informations forment un résumé de
toutes celles disponibles au niveau du site d’un cinéma, et elles sont de plus représentées différemment,
aussi bien au niveau de la structure des documents que du nom des balises utilisées. Le but est donc de
4 Différents choix techniques possibles pour les aspects d’échange et de communication seront étudiés à la fin de ce livre.
1.4. ÉCHANGE ET INTÉGRATION DE DONNÉES EN XML 43
Figure 1.18: Formulaire de saisie et page résultat fourni par le moteur de recherche
convertir le document XML pour chaque cinéma vers la structure définie pour le moteur de recherche, de
transférer cette conversion vers www.sallesenligne.com et enfin d’assembler tous ces fragments dans un
seul document sur lequel s’effectueront les opérations de recherche.
Cette expression indique que le contenu d’un élément " FILM # est constitué d’une succession d’éléments
dont les types respectifs sont TITRE, CINEMA, VILLE, URL et HEURE. La séparation des noms d’éléments
par des virgules signifie que l’ordre des éléments est fixé et doit correspondre à la séquence indiquée. Tous
les types d’éléments ne peuvent apparaître qu’une seule fois comme fils d’un élément " FILM # , sauf
l’élément de type URL qui est optionel (ce qui est indiqué par le symbol ?) et les éléments de type HEURE
qui peuvent apparaître plusieurs fois (indiqué par le symbole + après HEURE). Voici la DTD complète,
spécifiant les informations acceptées par le moteur de recherche :
44 CHAPTER 1. INTRODUCTION À XML ET XSLT
Il est possible d’indiquer, dans l’en-tête d’un document XML, à quelle DTD se conforme le contenu
du document. Cette information, quoique non obligatoire, est très importante car elle définit la classe
des applications qui vont pouvoir interpréter et traiter le document. Réciproquement, une application (par
exemple : un navigateur) est conçue pour traiter la classe de tous les documents XML conformes à une
DTD donnée (par exemple la DTD HTML).
Une feuille de style peut également être considérée comme un programme dédié à une DTD. C’est ce
que nous avons exprimé – de manière peu précise – en signalant qu’un programme de transformation du
document Alien.xml s’applique également à Vertigo.xml. De plus, si le résultat est toujours un document
XML conforme à une autre DTD, un programme XSLT peut être vu comme une conversion de documents
XML entre deux structures bien définies.
Les exemples de publications que nous avons décrits (HTML, WML, XSL-FO) étaient conformes à
ce cadre, puisque pour chacun de ces langages il existe une DTD. Maintenant, XML étant un langage
eXtensible, chacun peut définir sa propre DTD, et la publication XSLT s’étend à la mise en forme de nos
données afin qu’elles soient reconnues et traitées par quelqu’un d’autre. Le cas que nous traitons n’est
donc qu’une généralisation naturelle d’une problématique de publication qui se limiterait à HTML, ou aux
quelques langages largement diffusés.
<MOTEUR>
<FILM>
<TITRE>Alien</TITRE>
<CINEMA>Epée de bois</CINEMA>
<VILLE>Paris</VILLE>
<URL>www.epée-de-bois.fr/ExCinema1.xml</URL>
<HEURE>15:00</HEURE>
<HEURE>18:00</HEURE>
<HEURE>21:00</HEURE>
</FILM>
<FILM>
<TITRE>Vertigo</TITRE>
<CINEMA>Epée de bois</CINEMA>
<VILLE>Paris</VILLE>
<URL>www.epée-de-bois.fr/ExCinema1.xml</URL>
<HEURE>22:00</HEURE>
1.4. ÉCHANGE ET INTÉGRATION DE DONNÉES EN XML 45
</FILM>
</MOTEUR>
Afin d’obtenir un résumé du document EpeeDeBois.xml qui puisse être intégré dans le moteur de
recherche, il reste à lui appliquer une transformation à l’aide de la feuille de style suivante.
<xsl:template match="/">
<xsl:for-each select=’CINEMA/SALLE’>
<FILM>
<TITRE><xsl:value-of select=’FILM/TITRE’/></TITRE>
<CINEMA><xsl:value-of select=’../NOM’/></CINEMA>
<VILLE>Paris</VILLE>
<URL>www.epée-de-bois.fr/ExCinema1.xml</URL>
<xsl:for-each select=’SEANCES/SEANCE’>
<HEURE><xsl:value-of select=’.’/></HEURE>
</xsl:for-each>
</FILM>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Le mécanisme de transformation devrait maintenant être clair pour le lecteur. Notons que cette trans-
formation peut intervenir soit au niveau de chaque cinéma, qui fait alors l’effort de fournir un programme
de transformation de ses données en escomptant en tirer profit, soit au niveau du moteur de recherche
lui-même.
Exemple 16 MoteurHTML.xsl : Programme XSLT pour publier toutes les séances dans Moteur.xml.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head> <title>Résultat de la recherche</title> </head>
<body bgcolor="white">
<h1>Résultat de la recherche</h1>
<xsl:apply-templates select=’MOTEUR’/>
</body>
</html>
</xsl:template>
<xsl:template match="MOTEUR">
<xsl:for-each select="FILM">
<xsl:apply-templates select="." /><p/>
46 CHAPTER 1. INTRODUCTION À XML ET XSLT
</xsl:for-each>
</xsl:template>
<xsl:template match="FILM">
Ville: <i> <xsl:value-of select="VILLE"/> </i><br/>
Cinéma: <A HREF=’http://{URL}’>
<xsl:value-of select="CINEMA"/> </A> <br/>
Film: <xsl:value-of select="TITRE"/> <br/>
Séances:
<xsl:for-each select="HEURE">
<xsl:value-of select="."/> -
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Ce programme publie toutes les séances sous forme d’un document HTML. Il peut facilement être
adapté pour sélectionner un sous-ensemble des films à publier en utilisant des paramètres. En occurrence,
pour sélectionner les films à publier, on peut introduire trois paramètres de sélection $titre, $seance
et $ville dans la feuille de style, comme suit :
<xsl:param name="titre"/>
<xsl:param name="seance"/>
<xsl:param name="ville"/>
Ces déclarations globales sont ajoutées après l’élément " xsl:output method="html"/ # et
avant la première règle de transformation. Il suffit ensuite de modifier la règle de transormation pour les
éléments de type MOTEUR en ajoutant une condition de sélection avant la transformation d’un film :
<xsl:template match="MOTEUR">
<xsl:for-each select="FILM">
<xsl:if test=" ($titre = ’’ or TITRE = $titre)
and ($seance = ’’ or HEURE >= $seance)
and ($ville = ’’ or VILLE = $ville)">
<xsl:apply-templates select="." /><p/>
</xsl:if>
</xsl:for-each>
</xsl:template>
L’instruction xsl:if compare le contenu des éléments de type TITRE, HEURE et VILLE avec les
paramètres correspondants. Si la condition est satisfaite, l’élément de type FILM est transformé. Dans le
cas opposé, la boucle xsl:for-each passe au film suivant.
Les valeurs des paramètres formels $titre, $seance et $ville peuvent être obtenues de dif-
férentes manières. Dans le cas d’une application web, on utilise naturellement un formulaire HTML (fig-
ure 1.17) qui passe les paramètres dans l’URL de la feuille de style (méthode GET du protocole HTTP).
On obtient par exemple :
http://wwww.sallesenligne.fr/Moteur.xsl?titre=Vertigo&ville=Paris
En résumé, les programmes XSLT interviennent ici à deux niveaux : pour la transformation des docu-
ments issus de chaque site, en vue d’une uniformisation de la stucture des données telle qu’elle est définie
par la DTD du moteur de recherche, et pour l’extraction sélective de parties du document intégrateur à
l’aide de paramètres.
XML n’est pas un langage de programmation et ne permet donc pas directement d’exécuter des requêtes
SQL pour extraire les données de cette table. Il n’existe pas – pas encore ? – de norme pour définir comment
des parties programmées peuvent être intégrées dans du XML, mais le schéma généralement suivi est celui
illustré dans la figure 1.19.
1. des balises « dynamiques », interprétées par l’application comme correspondant à l’exécution de code
d’accès à une base de données, sont insérées dans le document initial ;
2. avant la transformation par une feuille XSLT, ces balises déclenchent le code qui va extraire des
informations et en faire un fragment XML ;
3. finalement ce fragment vient remplacer les balises « dynamiques » : un document intermédiaire est
obtenu, qui peut être publié avec XSLT.
Nous utilisons en l’occurrence les outils suivants : MySQL pour le SGBD, Java pour le code de con-
nexion et d’interrogation de la base, enfin XSP, un langage qui reprend le principe des JSP et permet
48 CHAPTER 1. INTRODUCTION À XML ET XSLT
Partie Partie
statique statique
Publication
Balises Code <fragment
"dynamiques" XML> XSLT
Partie SQL Partie
statique statique
Base
de données
Document initial Document intermédiaire
d’exécuter du code Java inclus dans un document XML. Le mécanisme peut être transposé, mutatis mu-
tandis, à d’autres environnement, par exemple l’environnement XML proposé par Oracle qui propose sen-
siblement le même schéma.
L’exemple développé ci-dessous va extraire la description XML d’un film et l’intégrer dans le document
XML correspondant aux séances de L’Épée de bois.
class ExJDBC
{
public static void main (String args []) throws SQLException
{
// Enregistrement du driver JDBC pour se connecter à
// MySQL
DriverManager.registerDriver(new org.gjt.mm.mysql.Driver());
// Connection à la base
try
{
Connection conn = DriverManager.getConnection
("jdbc:mysql://localhost/Films",
"visiteurFilms", "mdpVisiteur");
Les commentaires de ce programme devraient suffire à donner une compréhension, ne serait-ce qu’intuitive,
de son fonctionnement. Une requête (au sens large : interrogation ou mise à jour) correspond à un objet
de la classe Statement. Cet objet doit avoir été créé par un objet Connection, ce qui le rattache
automatiquement à l’une des transactions en cours.
La méthode executeQuery, comme son nom l’indique, exécute une requête (d’interrogation) placée
dans une chaîne de caractères. Le résultat est placé dans un objet ResultSet qui, comme son nom
l’indique encore une fois, contient l’ensemble des lignes du résultat.
Un objet ResultSet correspond à la notion de curseur employée systématiquement dans les inter-
faces entre un langage de programmation et SQL. La classe ResultSet propose un ensemble de méth-
odes get*** qui prennent un numéro d’attribut en entrée et renvoient la valeur de cet attribut. L’exécution
de ce programme donne le résultat suivant :
On est donc en présence d’une information transitoire, qu’il est possible d’intégrer à un document
statique avant d’effectuer la transformation XSLT.
Intégration Java/XML/XSLT
Maintenant que les films sont dans une base de données, il devient inutile de placer la description d’Alien
ou Vertigo dans des fichiers XML : il est bien préférable de créer à la demande la version transitoire XML,
puis d’appliquer la feuille de style existante.
Voici le nouveau document décrivant Alien, les éléments statiques étant remplacés par du code Java qui
les engendre dynamiquement.
<?cocoon-process type="xsp"?>
<?cocoon-process type="xslt"?>
<?xml-stylesheet href="Film.xsl" type="text/xsl"?>
<xsp:page
language="java"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
>
<xsp:structure>
<xsp:include>java.sql.*</xsp:include>
<xsp:include>java.util.Date</xsp:include>
</xsp:structure>
<FILM>
<xsp:logic>
DriverManager.registerDriver(new org.gjt.mm.mysql.Driver());
// Connection à la base
Connection conn = DriverManager.getConnection
("jdbc:mysql://localhost/Films",
"visiteurFilms", "mdpVisiteur");
Statement stmt = conn.createStatement ();
ResultSet rset = stmt.executeQuery (
"select * from Film where titre=’Alien’");
// Affichage du résultat
while (rset.next ())
{
<TITRE><xsp:expr>rset.getString (1)</xsp:expr></TITRE>
<ANNEE><xsp:expr>rset.getString (2)</xsp:expr></ANNEE>
<AUTEUR><xsp:expr>rset.getString (3)</xsp:expr></AUTEUR>
<GENRE><xsp:expr>rset.getString (4)</xsp:expr></GENRE>
<RESUME><xsp:expr>rset.getString (5)</xsp:expr></RESUME>
<PAYS><xsp:expr>rset.getString (6)</xsp:expr></PAYS>
}
</xsp:logic>
</FILM>
</xsp:page>
Il s’agit d’un document XML, avec un élément racine de type xsp:page et une nouvelle classe
d’éléments XSP dont toutes les balises étant préfixées par xsp:. On retrouve à peu de chose près la
structure du programme Java de l’exemple 17, avec, successivement :
3. enfin la production avec le éléments de type xsp:expr de chaînes de caractères qui viennent
s’intégrer au document XML.
Le document est traité en deux phases. Dans une première, le code Java est évalué et remplacé par les
chaînes de caractères produites, ce qui revient en l’occurrence à obtenir un fichier – temporaire – décrivant
le film Alien. Dans la seconde phase la feuille de style est apppliquée et on obtient la page HTML.
Le lecteur familier avec les Java Server Pages (JSP) notera que XSP s’en inspire largement : dans les
deux cas on inclut du code dans un langage de balisage, en limitant les parties programmées aux endroits
où des informations dynamiques doivent être insérées. La grande différence est qu’une page JSP mélange
HTML, Java, et même SQL. Ici on a séparé le contenu (en XML) de la présentation (obtenue par un
programme XSLT).
1.5. COMMENT LIRE LA SUITE DE CE LIVRE ? 51
<?cocoon-process type="xslt"?>
<?xml-stylesheet href="FilmXSP.xsl" type="text/xsl"?>
<FILM xmlns:BDFilm="http://cortes.cnam.fr/XBOOK">
<TITRE>Alien</TITRE>
<BDFilm:film titre=’Alien’/>
</FILM>
Cette fois plus aucun code n’apparaît dans le document qui est réduit à une extrême simplicité. Une
nouvelle classe de balises, BDFilm, a été définie pour servir d’interface avec la base de données. La balise
<BDFilm:film titre=’Alien’/>
exprime de la manière la plus simple qui soit une demande d’insertion de la représentation XML d’Alien.
Par un mécanisme propre au serveur d’application, et que nous ne décrirons pas ici, le processeur va
associer cette balise au code Java/JDBC présenté précédemment, constituer la représentation XML tempo-
raire du film, puis appliquer la transformation XSLT.
On obtient, avec cette architecture, une séparation complète des rôles :
1. la logique de l’application est codée en Java (ou tout autre langage reconnu par le serveur), ce qui
autorise tous les calculs, accès aux bases de données, échanges réseaux et appels de services divers
et variés ; l’information obtenue est rendue disponible par l’intermédiaire d’une librairie de balises
(taglib) ;
2. le contenu est intégré sous forme XML, à partir de sources d’informations qui peuvent être des
fichiers, des sites, ou les balises des librairies ;
Récapitulatif
En utilisant XML, nous rendons notre contenu indépendant du format propre à une application donnée.
Du même coup, nous dissocions ce contenu de toute signification intrinsèque. Un document XML n’a pas
d’autre interprétation que celle qu’un traitement particulier va lui associer à un moment donné. Prenons
deux exemples pour bien clarifier cette notion :
1. quand un traitement de texte sauvegarde un document dans son format propriétaire, ce format a une
signification, définie par le rendu qu’en fait le traitement de texte lors d’un affichage à l’écran ; il n’y
a rien de tel avec XML puisqu’un document est indépendant de toute application ;
52 CHAPTER 1. INTRODUCTION À XML ET XSLT
2. dans une page HTML, on trouve également des balises, mais la différence essentielle est que la
signification de ces balises est fixée, dans un contexte donné à l’avance qui est l’affichage dans la
fenêtre d’un navigateur : une balise " BR # correspond à un saut de ligne, et ne s’interprète pas
librement.
Ce qui compte en XML c’est la structure, à savoir l’arbre correspondant à un document, et l’utilisation
de noms d’éléments comme moyen pour différencier ou au contraire grouper de nœuds. En revanche le
choix des noms lui-même est sans importance et on aurait tout aussi bien pu utiliser pour décrire notre
cinéma les balises " A # , " B # , " C # et " D # ou les balises " MOVIE # , " NAME # , " ADDRESS # et
" SUBWAY # sans modifier la structure.
Chacun est donc libre de définir, en fonction de ses besoins, son propre langage basé sur XML (XML est
parfois présenté comme un méta-langage – un langage pour définir des langages). C’est ce que nous avons
fait pour décrire nos séances de cinéma, c’est ce que font également les navigateurs web avec (X)HTML,
les mobiles avec WML, etc. Tous ces langages partagent des règles de structuration communes, ce qui
permet de leur appliquer des outils standards (édition, analyse) et de passer de l’un à l’autre sans difficulté
majeure.
Publication XML/XSLT
Il existe des applications qui sont conçues pour travailler sur des documents XML ayant une structure
particulière, et utilisant une terminologie (un nommage des nœuds) spécifique. De telles applications
définissent la signification de tels documents, en fournissant un mise en forme (sur écran ou sur papier) ou
un mode de traitement (insertion dans une base de données, envoi de messages) de leur contenu.
L’exemple typique est fourni par un document HTML – ou, pour être plus précis, XHTML – qui obéit à
des règles de structuration et de nommage particuliers. Un document XHTML est un document XML, mais
doit impérativement avoir pour racine " HTML # , cette racine ayant elle-même des sous-éléments " HEAD #
et " BODY # (dans cet ordre), etc. Tout document conforme aux règles de structuration XHTML peut être
affiché dans un navigateur web qui interprète les balises du documents comme des commandes de mise en
forme à l’écran.
XHTML n’est qu’un des exemples possibles de « dialecte » XML, et la présentation de XML comme
un « super-HTML » ou comme un HTML « extensible » est selon nous une source de confusion. XHTML
ne constitue qu’un des nombreux exemples de format de données décrit par un langage plus général et
universel, XML. En d’autres termes, XML peut être considéré comme un outil pour définir des formats
de données qui sont liés à des applications spécifiques. Nous verrons dans ce livre plusieurs exemples de
dialectes XML, dont WML pour l’affichage sur des téléphones mobiles, RSS, une norme de description de
documents utiles pour les annuaires de liens, SMIL, un format dédié aux présentation multimédia, etc. Ces
dialectes de XML sont définis par des Document Type Definition (DTD).
À partir d’un document XML donné, il est possible d’effectuer des transformations du contenu afin
d’obtenir des représentations spécialisées qui peuvent alors être traitées par les applications. Il existe de
nombreuses techniques et outils pour effectuer de telles transformations. La perspective adoptée par ce
livre est celle d’une transformation avec des feuilles de style XSLT. Une feuille de style applique une
conversion à un document XML. Il est bien entendu possible de définir plusieurs feuilles de style pour un
même document, ce qui aboutit à un mécanisme de publication de contenu illustré dans la figure 1.20.
1.5. COMMENT LIRE LA SUITE DE CE LIVRE ? 53
Base de Message
Fichiers
données XML
Transformation
XSLT
XSLT Document XML
XSLT XSLT
XSLT XSLT
Contenu
Page XHTML Page WML Document XML
formaté
Cette figure décline quelques-unes des situations qui seront explorées dans les prochains chapitres.
Pour un même document XML, on obtient par des transformations XSLT :
En résumé, XML est un langage de description de données qui vise à l’universalité en se basant sur
un principe simple : le contenu est séparé de la présentation, et la manière dont ce contenu est constitué
est elle-même indépendante de la production d’une présentation. On obtient un processus de publication
en plusieurs étapes, chacune relevant d’une compétence particulière, ce qui constraste avec les techniques
courantes de productions de site qui impliquent la maîtrise et l’emploi simultané d’un grand nombre de
techniques et d’outils.
Sommaire
2.1 La syntaxe XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
2.1.1 Déclaration XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
2.1.2 Déclaration du type de document et des entités . . . . . . . . . . . . . . . . . . 57
2.1.3 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
2.1.4 Instructions de traitement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
2.1.5 Éléments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.1.6 Attributs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.1.7 Espaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
2.1.8 Sections CDATA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
2.1.9 Références d’entités . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
2.1.10 Balises, données caractères et valeur textuelle . . . . . . . . . . . . . . . . . . 62
2.2 Le modèle DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
2.2.1 Types de nœuds DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
2.2.2 Interface DOMString . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
2.2.3 Interfaces NodeList et NamedNodeMap . . . . . . . . . . . . . . . . . . . . . 66
2.2.4 Interface Node . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
2.2.5 Interface Document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
2.2.6 Interfaces Element et Attr . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
2.2.7 Interfaces DocumentType, Entity et EntityReference . . . . . . . . . . . . . 74
2.3 Du document sérialisé à l’arbre DOM . . . . . . . . . . . . . . . . . . . . . . . . . 74
2.3.1 Construction d’un arbre DOM . . . . . . . . . . . . . . . . . . . . . . . . . . 76
2.3.2 Traitement des espaces pendant la construction . . . . . . . . . . . . . . . . . . 78
2.3.3 Deux fonctions de navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
2.4 Le langage XPath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
2.4.1 Représentation XPath d’un document XML . . . . . . . . . . . . . . . . . . . 82
2.4.2 Expressions XPath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
2.4.3 Les axes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
2.4.4 Les filtres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
2.4.5 Prédicats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
2.4.6 Types et opérations XPath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
2.4.7 Exemples d’expressions XPath . . . . . . . . . . . . . . . . . . . . . . . . . . 107
55
56 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Nous approfondissons dans ce chapitre la notion de document XML, en mettant l’accent sur la structure
d’un document et sur la navigation dans cette structure. Ces deux aspects sont en effet à la base de la
programmation XSLT.
Nous suivons la démarche adoptée dès l’introduction, consistant à considérer un document XML
comme un arbre. Ce point de vue a le grand avantage d’être indépendant de l’origine du document et de
faire abstraction de sa représentation physique ou textuelle : la représentation arborescente est conceptuelle
et a pour but de décrire sans ambiguïté la structure et le contenu d’un document. Ce n’est pas le cas d’une
représentation textuelle ou « sérialisée » où un même contenu peut être représenté avec des constructions
syntaxiques différentes. Par exemple un élément vide a une seule représentation dans l’arborescence (un
nœud sans fils) mais possède deux représentations textuelles équivalentes : " a #%" /a # et " a/ # . Un
autre exemple d’une tel ambiguïté est le caractère ’>’ qui peut-être représenté par ’>’ et ’>’ dans
un document XML.
Nous décrirons la structure arborescente d’un document XML en nous appuyant sur un modèle normal-
isé de représentation et de manipulation de données XML, le modèle DOM. Dans la suite, et quand il est
nécessaire de distinguer explicitement les deux types de représentation, nous utiliserons le terme document
sérialisé pour désigner la représentation textuelle d’un document XML et le terme arbre DOM pour sa
représentation arborescente.
Avant d’être transmis à un traitement, quel qu’il soit, un document XML (sérialisé) est traité par un
processeur XML ou parseur qui va analyser son contenu et déterminer sa structure. Dans le cas d’un
parseur DOM le résultat de cet analyse est un arbre DOM. Dans tous les cas, cette phase d’analyse peut
échouer si le document n’est pas bien formé, autrement dit s’il ne respecte pas la syntaxe du langage XML.
Bien que cette syntaxe ne soit pas nécessaire pour expliquer le fonctionnement de XSLT proprement dit,
il est important de bien comprendre comment un document XML est transformé en arborescence DOM et
inversement.
Nous utiliserons également le modèle DOM (Document Object Model) pour compléter notre descrip-
tion de la structure d’un document par les opérations de navigation dans ce document. DOM fournit une
spécification orientée-objet basée sur des méthodes d’investigation et d’extraction de données. Bien que
cette spécification définisse de manière précise les opérations sur un arbre XML, sa mise en œuvre en pra-
tique nécessite le recours à un environnement de programmation comme Java. Le langage XPath, étudié
dans la seconde partie de ce chapitre, offre un outil plus simple pour déclarer des chemins complexes
d’accès à des éléments. C’est XPath qui est utilisé dans XSLT, et pas les opérations DOM, mais il est
important de signaler dès maintenant que l’évaluation d’un programme XSLT peut être décrite en DOM
(même si le système lui-même n’utilise pas nécessairement cette API), et que la compréhension de ce
modèle donne donc la clé d’une bonne interprétation des règles XSLT.
<A>
Du &monTexte;, sans caractères réservés:
ni < ni > ni & ni ' ni "
<B> contenu texte </B>
<!-- Un élément vide -->
<C/>
<D attr1="1" attr2="azerty">
<B>Encore &monTexte;,</B>
</D>
<![CDATA[Du texte <non interprété> &monTexte;]]>
&maSignature;
</A>
Cet exemple va nous servir de support à la présentation des différents composants d’un document XML.
<?xml version="1.0"?>
Cette information est principalement destinée au traitement qui va analyser le document, afin qu’il
puisse tenir compte des éventuelles variantes de syntaxe entre les différentes versions XML. On peut aussi
lui associer des attributs (voir plus loin) caractérisant le document. Nous utilisons par exemple systéma-
tiquement l’attribut encoding avec la valeur ISO-8859-1 qui indique un codage de caractères « latin »
incluant les caractères accentués.
L’attribut (optionnel) standalone indique si le document XML contient des références vers d’autres
fichiers ou « entités externes » (voir plus loin) qui doivent être chargées pendant l’analyse du document. La
valeur yes permet au processeur XML de savoir qu’il n’est pas nécessaire d’accéder à d’autres sources
pour analyser et restituer le document XML en entier. Dans notre exemple nous avons défini stan-
dalone=’no’ (qui est aussi la valeur par défaut) car, comme nous allons le voir maintenant, il contient
une référence vers une entité externe.
La déclaration de document est uniquement destinée au parseur, et n’est plus « visible » dans l’arborescence
DOM d’un document XML. On ne peut donc y avoir accès dans un traitement XSLT.
Contrainte : La déclaration XML est optionnelle, mais sa présence est cependant recommandée. Si elle
est présente, elle doit être la première ligne du document.
répertoire à l’autre. Pour remédier à ce problème, XML propose également l’utilisation « d’identificateurs
publics », qui seront décrits dans le chapitre 4.
Les entités fournissent un moyen de factoriser des fragments XML et des réutiliser en plusieurs en-
droits. Dans notre exemple, les déclarations d’entités sont locales (comprises entre les crochets [ ]) mais
il peuvent également faire partie du fichier externe sourceDTD. Notre document ExXML4.xml, page 56,
contient la déclaration d’une entité interne (une simple chaîne de caractères) et d’une entité externe (un
fichier) :
Le nom de la première entité est monTexte et sa valeur texte simple. La deuxième entité a le
nom maSignature et sa valeur est définie par le contenu du fichier signature.xml. Les deux entités
peuvent ensuite être référencées dans le document par leur nom.
Contrainte : La déclaration du type de document est optionnelle. Si elle est présente, elle doit apparaître
avant le premier élément du document.
2.1.3 Commentaires
Un commentaire est une zone de texte libre encadrée par " !-- et -- # . Il peut contenir n’importe quel
texte, à l’exception de --, selon n’importe quelle disposition (sauts de lignes par exemple). Le commen-
taire suivant est syntaxiquement correct (bien formé) :
Le deuxième commentaire n’est pas bien formé, car un commentaire ne doit pas terminer par « ---> ».
Contrainte : Les commentaires peuvent être placés n’importe où dans le document, sauf dans une balise.
Le mot qui suit le « ? » est la cible (target), le reste constituant le contenu de l’instruction de traitement.
Il faut noter que la déclaration de document XML, bien que conforme à la définition syntaxique, n’est pas
une instruction de traitement. De plus, le nom de cible ’xml’ (en minuscule, majuscule ou mélangé) est
réservé pour des versions futures du standard XML.
Contrainte : Les instructions de traitement peuvent être placés n’importe où dans le document, sauf dans
une balise.
2.1. LA SYNTAXE XML 59
2.1.5 Éléments
Les éléments constituent le principal composant d’un document XML. La représentation sérialisée de
tout élément se compose d’une balise ouvrante " balise # , de son contenu et d’une balise fermante
" /balise # . La présence des balises ouvrantes et fermantes est obligatoire. En revanche le contenu
d’un élément peut être vide. Il existe alors une convention qui permet d’abréger la notation. L’élément vide
<C></C>
Le contenu d’un élément peut aussi – et surtout – être constitué d’une combinaison arbitrairement
complexe de commentaires, d’autres éléments, de références entités et de sections de texte (voir plus loin).
Par exemple, le contenu de l’élément de type A dans le fichier ExXML4.xml est une telle combinaison de
catégories syntaxiques.
Contrainte : Tout document comprend un et un seul élément racine qui définit le contenu même du doc-
ument.
Notre exemple ExXML4.xml contient un élément racine de type A. Le document suivant n’est pas bien
formé car il a deux éléments racine.
<?xml version="1.0"?>
<A>Un premier contenu</A>
<B>Un deuxième contenu</B>
Contrainte : Le nom du type de l’élément racine doit être identique au nom du type de document (si
celui-i est présent).
Le document suivant n’est pas bien formé car le type de l’élément ne correspond pas au type du docu-
ment :
<?xml version="1.0"?>
<!DOCTYPE A SYSTEM "fichier.dtd">
<B>contenu</B>
Le nom d’une balise, qui correspond au type de l’élément (voir plus loin), peut comprendre des lettres
de l’alphabet, des chiffres, les caractères « - », « _ », mais pas de blanc ni de lettres accentuées. De plus
le nom ne doit pas commencer par un chiffre. Enfin XML distingue les majuscules des minuscules, et la
balise " NOM # sera donc considérée comme distincte de la balise " nom # (ces deux balises définissent
deux éléments de types différents). Sous réserve de ces quelques restrictions, les noms d’élément sont
libres en XML : il n’existe pas de nom réservé.
2.1.6 Attributs
Un élément peut avoir des attributs. Les attributs apparaissent toujours dans la balise ouvrante, sous la
forme nom="valeur" ou nom=’valeur’, séparés par un espace blanc (voir plus loin). Les noms
d’attributs suivent les mêmes règles que les noms d’éléments.
Contrainte : La balise ouvrante d’un élément ne doit pas contenir deux attributs avec le même nom.
Pour ceux qui auraient l’habitude des pratiques assez laxistes de HTML, il est bon de noter que
! un attribut doit toujours avoir une valeur, donc la balise HTML " OPTION SELECTED # n’est pas
du XML bien formé ;
! la valeur doit toujours être comprise entre des apostrophes simples (’10’) ou des guillemets ("10") ;
la valeur elle-même peut contenir des apostrophes simples si elle est encadrée par des guillemets, et
réciproquement ;
Une caractéristique essentielle de XML est que l’ordre des attributs d’un élément n’est pas significatif.
L’élément :
Autrement dit tout traitement doit donner le même résultat, indépendamment de l’ordre des attributs.
En pratique il faudra toujours accéder à un attribut par son nom, et pas par sa position.
Il existe plusieurs noms d’attribut réservés dans XML (tous commencent par le préfixe xml:) :
! xml:lang : cet attribut permet d’indiquer la langue utilisée dans le contenu d’un élément. Les
codes utilisés pour les langues sont définis par le standard IETF RFC 1766. Par exemple, les codes
en, fr et de indiquent respectivement des contenus en Anglais, Français et Allemand.
! xml:space : cet attribut précise le traitement des espaces dans le contenu d’un élément. Il a deux
valeurs possibles, default ou preserved. Dans le premier cas, c’est l’application qui détermine le
traitement des espaces. Dans le second cas, tous les espaces doivent être préservés par l’application.
! le nom d’attribut xmlns et tous les noms qui commencent par xmlns: sont également réservés
pour la spécification d’espaces de nom.
Remarque : Les espaces de nom (namespace) constituent un aspect important de XML : ils seront décrits
de manière approfondie dans le chapitre 6.
2.1.7 Espaces
Les espaces jouent un rôle spécial dans la représentation syntaxique d’un document XML. Un « espace » est
une chaîne de caractères composée uniquement de caractères espace blanc (Unicode #x20), retour chariot
(Unicode #x9), line feed (Unicode #xD) et tabulation (Unicode #xA) (voir page 64 pour plus de détails sur
le standard Unicode).
Généralement les espaces blancs servent comme séparateurs entre les différents composants syntax-
iques d’un document sérialisé. Par exemple, le fichier ExXML4.xml contient 18 lignes, c’est à dire 18
retours chariot. On voit également que, pour augmenter la lisibilité, le même document contient des es-
paces (#x20) pour souligner la structure hiérarchique du document.
À priori un parseur XML doit transmettre tous les espaces à l’application exploitant le document (par
exemple au processeur XSLT). Néanmoins, un programme XSLT permet de spécifier une prise en compte
spécifique des nombreux espaces apparaissant dans un document XML. Il est courant par exemple d’ignorer
tous les espaces en dehors de l’élément racine d’un document ou des éléments qui contiennent uniquement
un espace blanc. Il est surprenant de constater que la gestion des espaces constitue un point très délicat
dans la manipulation de documents XML. Nous aurons donc souvent l’occasion de revenir sur le traitement
des espaces et la la distinction entre espaces significatifs et non-significatifs.
2.1. LA SYNTAXE XML 61
1. quand on veut inclure de code de programmation qui contient des caractères réservés dans XML : les
’ " ’ et ’&’ abondent dans du code C ou C++ ;
2. quand le document XML est consacré lui-même à XML, avec des exemples de balises que l’on
souhaite reproduire littéralement.
Les sections littérales CDATA définissent une zone qui n’est pas analysée par le parseur, et est donc
transmise telle quelle au programme traitant le document XML (par exemple un programme XSLT). Notre
petit exemple contient une section CDATA :
Il est clair que le traitement de ce contenu mènerait à résoudre les références à l’entité monTexte, et à
tenter d’interpréter " non interprété # comme une balise, ce qui d’ailleurs échouerait car il ne s’agit
pas d’un nom d’élément valide.
Cela signifie que nous n’avons pas à nous soucier des entités dans nos programmes XSLT puisque
ceux-ci travaillent sur la représentation du document obtenue à l’issue de la phase de parsing, et donc après
résolution des références aux entités.
Remarque : Le remplacement des références vers les entités par leur valeur n’est pas une obligation im-
posée par la recommandation XML. L’avantage d’un tel remplacement est une programmation plus facile
qui n’est pas obligée de se préoccuper de la composition physique d’un document XML.
La syntaxe XML permet de spécifier des caractères par leur code Unicode. Ainsi, au lieu d’écrire C
en majuscule, on peut également utiliser la référence C où l’entier 67 correspond à l’Unicode de ce
caractère. Ce mécanisme de référencement est utile& pour inclure des caractères qui ne se trouvent pas sur
le clavier qu’on utilise. Par exemple, le caractère « c » dont le code est soit 169, soit xA9 en hexadécimal
( ')(+*'-,+.0/213'),4/ , le symbole « x » après ’#’ indiquant que le code est en hexadécimal) peut être inclus
dans un document XML par des références vers des entités caractères #169; ou #xA9;.
Mais les références vers les entités caractères sont surtout utiles pour inclure des caractères avec une
signification spécifique dans la syntaxe XML : « < », « > », « & », « ’ » et « " ». Par exemple, pour inclure
le symbole « < » dans le texte sans qu’il soit interprété par le processeur XML comme le début d’une balise
on peut utiliser la référence <.
À priori, toutes les entités référencés dans un document XML doivent être déclarées dans sa DTD. Il
existe deux exceptions : les entités caractères et les entités du tableau 2.1 si le document n’a pas de DTD :
Notre exemple contient le fragment suivant :
62 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Table 2.1: Entités caractères prédéfinies dans un document XML sans DTD
Ce qui donnera, après résolution des références aux entités le texte suivant :
Du texte simple, sans caractères réservés:
ni < ni > ni & ni ’ ni "
Les entités externes permettent quant à elles d’inclure le contenu d’un fichier.
<!ENTITY maSignature SYSTEM "signature.xml">
On peut donc insérer le contenu de signature.xml par une simple référence &maSignature;. Ce mé-
canisme permet de constituer un document par assemblage de plusieurs autres, éventuellement accessibles
sur le réseau. Par défaut, on suppose que le fichier inclus se trouve au « même endroit » que le document
principal, mais il est également possible de définir un chemin d’accès différent à travers une URL absolue.
XML distingue entre deux types d’entités externes : les entités XML (parsed entities) et les entités
non-XML (unparsed entities) :
! Une entité XML doit être conforme à la définition du contenu d’un élément. Ainsi une entité externe
peut être un document XML bien-formé mais aussi un fragment XML avec plusieurs (ou sans) élé-
ments racines mélangés avec des sections de texte et des commentaires. Ce fragment doit néanmoins
être bien-formé et « complet » dans le sens où toutes les balises ouvrantes sont fermées dans la même
entité.
! Une entité non-XML peut être un fichier avec n’importe quel contenu comme par exemple une image
ou un fichier audio. Ce contenu ne respecte évidemment pas la syntaxe XML ce qui a comme con-
séquence, qu’il ne peut pas faire partie du contenu d’un élément. XSLT ne prévoit pas un traitement
spécifique pour les références vers les entités non-XML dans un document XML.
Une entité externe peut contenir des références vers d’autres entités et, pour chaque document XML,
on peut créer une arborescence d’entités (plus précisément un graphe acyclique) qui se réfèrent. La racine
de cette arborescence est le document XML, également appelé entité document.
Les données caractères représentent toute la partie textuelle d’un document XML qui n’est pas inter-
prétée par un parseur. Par exemple, les données caractères du document ExXML4.xml sont les suivantes
(les espaces sont représentés par « _ » et les retours chariots par « | ») :
|
|
__|
__|
|
|
|
|
__Du_,_sans_caractères_réservés:|
__ni__ni__ni__ni__ni_|
___contenu_texte_|
___Un_élément_vide_|
__|
__|
___Encore_,|
__|
__Du_texte_<non_interprété>_&monTexte;|
__|
On voit que les retours chariots et les espaces font partie des données caractères d’un document et que les
références vers les entités ont disparu. Ainsi, les données caractères ne correspondent pas exactement au
contenu « textuel » d’un document s’il contient des références vers des entités. Pour faire cette distinction,
nous allons utiliser le terme valeur textuelle pour désigner les données caractères d’un document (ou d’un
élément) une fois remplacées toutes les références aux entités par leur valeur. Par exemple, la valeur
textuelle de l’élément A dans le document ExXML4.xml est :
|
__Du_texte_simple,_sans_caractères_réservés:|
__ni_<_ni_>_ni_&_ni_’_ni_"|
___contenu_texte_|
__|
__|
__|
___Encore_texte_simple,|
__|
__Du_texte_<non_interprété>_&monTexte;|
__signature|
Dans la valeur textuelle de l’élément toutes les références ont été remplacées par leur valeur (nous avons
supposé que le fichier signature.xml contient uniquement la chaîne de caractères « signature »). On
peut remarquer que le contenu d’une section CDATA correspond à sa valeur textuelle.
Un autre exemple de la différence entre les données caractères dans un document ou d’un élément et
sa valeur textuelle concerne les espaces. À priori un parseur XML doit transmettre tous les espaces qui ne
font pas partie de sections de balisage à l’application exploitant le document (par exemple au processeur
XSLT). Néanmoins, les espaces peuvent interprétés différemment selon l’endroit où ils apparaissent.
64 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Par exemple, les espaces qui se trouvent avant l’élément racine et ne font pas partie du contenu du
document ont « moins » de signification que ceux qui se trouvent dans le contenu d’un élément. Nous
reviendrons plusieurs fois sur le traitement des espaces blancs dans XML et XSLT (pages 74 et chapitre 3).
Remarque : La notion de valeur textuelle ne fait pas partie de la recommandation XML, mais a été intro-
duite dans XSLT.
1 La
présentation qui suit correspond au DOM (Core) Level 1.
2 Comme Java, DOM utilise la notion d’interface pour souligner l’indépendance d’un langage de programmation et d’une implan-
tation spécifique. Nous allons utiliser dans la suite le terme type DOM pour souligner les aspects structuraux et interface DOM pour
les aspects liés à la programmation.
2.2. LE MODÈLE DOM 65
La plupart de ces interfaces (onze sur treize) correspondent aux différents catégories syntaxiques XML
que nous avons décrites page 56. Les seules exceptions sont DocumentFragment et Notation :
! L’interface DocumentFragment est essentiellement destinée à faciliter la programmation et corre-
spond à une version allégée de l’interface Document. Elle peut être ignorée sans dommage dans le
contexte XSLT.
! L’interface Notation correspond à une notion faisant partie de la DTD du document et elle permet
à une application XML de choisir les actions pour traiter des données non-XML dans le contenu du
document.
Tous ces interfaces sont considérées par DOM comme des spécialisations d’une interface Node dont ils
héritent toutes les propriétés et méthodes. La figure 2.1 montre cette hiérarchie de spécialisation (l’interface
DocumentFragment n’apparaît pas dans la figure). Les interfaces TreeNode, Leaf et Container montrées
Node
Attribute TreeNode
Leaf Container
Comment Text
CData
Section
dans la figure 2.1 ne font pas partie du modèle DOM, mais nous les avons ajoutées pour clarifier le rôle des
différents interfaces dans une arborescence DOM :
! l’interface TreeNode permet de différentier les attributs XML qui correspondent à des nœuds de type
Attr des autres nœuds d’une arborescence DOM : nous revenons sur cette question ci-dessous ;
! parmi les nœuds de type TreeNode nous distinguons ceux qui acceptent des fils (type Container), et
ceux qui sont forcément des feuilles de l’arbre (type Leaf ).
Le modèle DOM spécifie une interface de programmation qui permet soit de modifier un document,
soit de l’inspecter en vue d’en extraire des informations, point de vue que nous privilégions. Toutes ces
opérations sont disponibles sous forme de propriétés3 , et de méthodes propres à chaque type de nœud.
3 Nous utilisons le mot propriété au lieu de celui, plus couramment employé, d’attribut afin de ne pas introduire de confusion avec
Avant d’y revenir en détail, nous introduisons trois interfaces supplémentaires qui définissent quelques
structures de données nécessaires à représentation complète d’une arborescence DOM. Ces interfaces ne
sont pas représentées dans la hiérarchie de la figure 2.1.
Dans DOM, la comparaison de deux chaînes de caractères s’effectue sur la représentation Unicode de
leurs caractères et prend donc en compte ainsi la casse des chaînes comparées.
Propriété Type
nodeType unsigned short
nodeName DOMString
nodeValue DOMString
parentNode Node
firstChild Node
lastChild Node
childNodes NodeList
previousSibling Node
nextSibling Node
attributes NamedNodeMap
d’efficacité de l’implantation. On peut donc accéder à toutes les opérations nécessaires au niveau du type
Node, à charge pour le programmeur de tester le type d’un objet appartenant à l’interface générique Node
pour savoir quelles sont les opérations appropriées (technique dite de casting, à éviter en principe dans une
approche objet). La propriété NodeType est justement destinée à effectuer ce test.
Cette conception constitue une limite de DOM, modèle qui se trouve à mi-chemin entre une mod-
élisation conceptuelle rigoureuse et indépendante de l’implantation, et des compromis techniques im-
posées par des critères d’efficacité ou de facilité de développement. L’inconvénient est qu’il faut connaître
l’interprétation des propriétés en fonction du type de nœud, ce que présente le tableau 2.4.
La figure 2.2 montre une instance d’un document DOM, avec des choix de représentation que nous
adopterons dans toute la suite de ce livre et qu’il est bon de souligner dès maintenant.
Dans chaque nœud figurent trois informations : le type DOM (première ligne en gras), son nom (deuxième
ligne) et la valeur (troisième ligne). Comme souligné précédemment, le nom et la valeur ne sont pertinents
que pour certains types de nœuds. Par exemple les nœuds DOM de type Text ont une valeur mais pas
de nom, ce que nous représentons par un symbole « – » dans la deuxième ligne, et, à l’inverse, les nœuds
DOM de type Element ont un nom, mais pas de valeur. Dans ce cas la troisième ligne est absente.
L’arbre DOM de la figure 2.2 contient neuf nœuds dont deux sont des attributs. Pour simplifier la
description de cet arbre, nous identifions chaque nœud par un entier ou un caractère (s’il s’agit d’un attribut)
écrit entre parenthèses à côté du type du nœud. La racine (nœud 1) du document est de type Document
et contient un fils unique (2) de type Element. L’élément racine a deux attributs (notés a et b) et deux
sous-éléments (3 et 5) de type Element. Le premier élément, de type B (il ne faut pas confondre le type
68 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Document (1)
-
Element (2)
A
d’un nœud DOM avec le type d’un élément) et contient un seul nœud de type Text ; le deuxième élément
contient un commentaire et du texte. Si on parcourt cet arbre à partir de la racine, en allant de gauche
à droite et en profondeur d’abord, on obtient la séquence de nœuds 1 2 3 4 5 6 7 (on remarque que les
attributs n’apparaissent pas dans ce parcours).
childNodes[]
childNodes[]
Leaf Container
Le tableau 2.5 montre la « valeur » de ces propriétés pour quelques nœuds de l’arbre DOM présenté dans
la figure 2.2 : pour les propriétés parentNode, firstChild, lastChild previousSibling et nextSibling la valeur
correspond à une référence vers un nœud représenté par son identificateur. La propriété childNodes retourne
une liste de nœuds (NodeList) qui est représentée par une séquence d’entiers entre crochets. Finalement,
4 Le mot sibling en anglais désigne indifféremment le frère ou la sœur, et n’a malheureusement pas d’équivalent en français. Nous
la propriétés attributes retourne un ensemble d’attributs (NamedNodeMap) que nous représentons sous
forme d’un ensemble d’identificateurs d’attributs (un ensemble est noté {} pour le différentier d’une liste).
Bien entendu, tout ou partie de ces valeurs peuvent être à NULL (non définies) : il ne peut pas y avoir
de père pour un nœud de type Document, pas de fils, de frère précédent ou suivant pour tous les nœuds de
type Leaf.
Toutes les propriétés structurelles précédentes s’appliquent à tous les nœuds DOM (même les attributs)
et contiennent comme valeur pour les nœuds qui ne sont pas de type Container des listes vides (par exemple
pour childNodes) ou la valeur NULL. Il faut noter que la propriété parentNode n’est pas définie pour les
nœuds de type Attribute.
Des propriétés comme firstChild ou lastChild ne font jamais référence à des attributs. Le dernière
propriété de l’interface Node que nous n’avons pas détaillé est attributes. Cette propriété, seulement définie
pour les éléments, est de type NamedNodeMap qui fournit un ensemble d’opérations pour inspecter et
mettre à jour les attributs d’un élément.
Remarque : Bien que l’interface NamedNodeMap permette de choisir un attribut par son nom (méthode
getNamedItem()) et par sa position (méthode item()), rappelons que les attributs d’un élément ne
sont pas ordonnés. En d’autres termes, si deux éléments se distinguent seulement par l’ordre des attributs
dans la propriété attributes, ils sont considérés comme identiques.
Passons maintenant aux opérations du type Node, données dans le tableau 2.6. Elles permettent de modifier
un document en ajoutant, remplaçant et supprimant les fils d’un nœud. Les méthodes hasChildNodes()
et hasAttributes() permettent de vérifier l’existence de fils ou d’attributs et la méthode cloneN-
ode() permet de créer une copie d’un nœud (ainsi que de tous ses descendants si la valeur du paramètre
booléen prof est true).
Certaines de ces opérations ne sont pas autorisées sur certains types de nœuds, comme par exemple
l’insertion d’un fils pour un nœud de type Text. Il est donc de la responsabilité de celui/celle qui programme
un traitement de s’assurer qu’aucune opération interdite ne pourra être déclenchée par le traitement, ce qui
là encore montre que DOM ne s’appuie que partiellement sur les concepts orientés-objet.
70 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
La fonction parcours_préordre() prend un nœud DOM comme argument et utilise les propriétés
nodeName et childNodes et la méthode booléenne hasChildNodes() de l’interface Node. La propriété
id ne fait pas partie de l’interface Node et elle donne les identificateurs des nœuds que nous avons introduit
plus haut.
L’application de cette fonction à un nœud imprime d’abord l’identificateur du nœud suivi d’un espace,
vérifie ensuite si le nœud a des fils et, si c’est le cas, effectue un parcours pour chacun des fils dans l’ordre
de la liste childNodes. Le résultat affiché par la fonction parcours_préordre() pour tous les nœuds
dans l’arbre de la figure 2.2 est donné dans le tableau 2.7.
Pour les feuilles (4, 6 et 7), la fonction affiche uniquement leurs identificateurs. Pour les nœuds 3
et 5, elle affiche l’identificateur du nœud suivi de ses enfants (qui sont des feuilles). En remontant dans
2.2. LE MODÈLE DOM 71
l’arborescence on arrive à la racine qui produit l’affichage de tous les nœuds du documents (sauf les at-
tributs) dans l’ordre.
Comme nous l’avons déjà remarqué, l’ordre des nœuds est significatif (toujours en excluant les attributs
XML). Deux documents ayant les mêmes nœuds mais pas dans le même ordre seront donc considérés
comme différents. Cette notion d’ordre est reflétée par le type de la propriété childNodes qui est une
liste de nœuds et permet de distinguer les fils d’un élément par leur position (le premier fils, le deuxième,
etc. . . ). Ceci signifie, en pratique, qu’un traitement qui parcourt la liste des fils d’un élément ne donnera
pas forcément le même résultat si l’ordre vien à changer.
Reprenons, par exemple, l’arbre de la figure 2.2, mais en changeant l’ordre des fils de la racine et de
ceux du nœud 5 (la figure 2.4).
Document (1)
-
Element (2)
A
Les nœuds qui précèdent l’élément racine constituent le prologue du document XML, et les nœuds qui
suivent l’épilogue.
Nom Type
doctype DocumentType
implementation DocumentImpl
documentElement Element
Le tableau 2.9 donne les propriétés du type Document. La propriété doctype est de type Document-
Type et contient la DTD du document XML. Elle est à NULL si la DTD est absente. La propriété imple-
mentation est utilisée pour référencer l’objet « créateur » du document de type DocumentImpl. Souvent,
mais pas obligatoirement, cet objet correspond au parseur qui génère une représentation DOM à partir d’un
document XML. Finalement, la propriété documentElement référence l’élément racine du document.
Un nœud de type Document fournit des opérations permettant de créer tous les autres nœuds d’un arbre
DOM. Ce type d’objet agit donc comme une « fabrique » des différents types de nœuds d’une arborescence
DOM. Le tableau 2.10 donne quelques-unes de ces opérations :
Chaque méthode createX() crée un objet de type X à partir des paramètres qui sont fournis sous
forme de chaînes de caractères de type DOMString (voir section 2.2.2). Nous avons vu dans la section 2.1
que ces chaînes de caractères doivent respecter quelques contraintes quand il s’agit d’un nom d’élément,
d’un nom d’attribut ou d’une cible dans une instruction de traitement (ProcessingInstruction). En partic-
ulier, ils ne doivent pas contenir d’espaces.
Pour toutes ces raisons, on peut considérer chaque élément à la foi comme un nœud dans un arbre
DOM, et comme la racine d’un arbre non-ordonné dont les fils sont des attributs. Ces deux rôles d’un
élément sont indépendants : on ne mélange jamais les fils « DOM » d’un élément et ses fils de type Attr.
Cette distinction est illustrée dans la figure 2.5 par la représentation de deux compositions différentes.
attributes{}
Element Attribute
childNodes[]
TreeNode
Les opérations définies pour les éléments sont données dans le tableau 2.11.
L’opération de normalisation est utile si on a ajoute plusieurs nœuds de type Text consécutifs comme
fils d’un élément : dans la version sérialisée du même élément, ces fils consécutifs ne peuvent plus être
distingués l’un de l’autre. Ainsi, l’application de la méthode normalise() à tous les éléments d’un
arbre DOM donne un nouvel arbre qui correspond exactement à l’arbre qu’on obtient par une sérialisation
suivie d’une analyse par un parseur DOM. Cela peut être important dans le cas où on utilise des opérations
qui dépendent du nombre ou de la position des fils d’un élément.
74 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Le premier attribut est défini dans l’élément F A E . Son nom est at1 est sa valeur est val1. Cet
élément a deux fils F B E et F C E dont le premier contient un texte et le deuxième contient de nouveau
deux fils avec un attribut at2 chacun. L’arbre DOM correspondant est montré dans la figure 2.6.
Les attributs sont également représentés sous forme de nœuds dans l’arbre DOM (figure 2.6). Nous
leur affectons une représentation différente à cause de leurs caractéristiques spécifiques.
Voici un autre exemple avec une instruction de traitement et un commentaire :
Exemple 23 ExXML2DOM2.xml : Document XML avec instructions de traitement et commentaires
<?xml version=’1.0’ encoding="ISO-8859-1"?>
<?xml-stylesheet href="feuille.xsl" type="text/xsl"?>
<A>
<C>
<!-- un commentaire -->
<D at1="1">un < et un ></D>
</C>
</A>
2.3. DU DOCUMENT SÉRIALISÉ À L’ARBRE DOM 75
Document
-
Element
A
Ce document contient une instruction de traitement dans la deuxième ligne – bien que syntaxiquement
conforme à la définition, la première ligne du document n’est pas une instruction de traitement. La cible
de cette instruction est xml-stylesheet et elle indique au processeur XSLT l’URL du programme et
le type du transformation à appliquer. Enfin le commentaire dans la cinquième ligne est une description
courte de l’élément qui suit. Les instructions de traitement et les commentaires sont également représentés
sous forme de nœuds dans l’arbre DOM (figure 2.7). On peut également constater que les références <
et > on été remplacées par leurs valeurs respectives dans le fils de type Texte de l’élément D.
Document
-
Instruction
xml-stylesheet Element
A
href="feuille.xsl" type="text/xsl"
Element
C
Comment Element
- D
un commentaire
Attr Text
G H
at1 -
1 un et un
Au moment de l’analyse, le processeur se base sur des caractères réservés comme F , E et & pour
structurer le document. En contrepartie le contenu textuel ne peut pas contenir de balises, ni même de
caractères comme F ou E puisque ceux-ci vont être interprétés comme faisant partie du marquage. Les
sections CDATA permettent de prévenir cette interprétation.
Dans l’exemple ci-dessus, le contenu textuel du document XML comprend le contenu d’un nœud de
type Text (« Voici l’exemple d’un document XML ») et le contenu d’une section CDATA. La figure 2.8
montre l’arbre DOM correspondant.
Document
-
Element
A
CDATASection
-
G ?xml version=’1.0? H
Text
- G H A Text
-
G H
B <B> G/B H
</B>
G /AH
Figure 2.8: Représentation DOM avec un nœud de type CDATASection
Le document XML est représentée linéairement sous la forme d’une chaîne de caractères, et structuré
à l’aide de balises qui peuvent être distinguées des informations textuelles par les symboles F et E qui les
entourent. La construction de l’arbre DOM consiste à parcourir cette chaîne séquentiellement en appliquant
l’algorithme ci-dessous, décrit à l’aide des opérations DOM qui ont été présentées dans la section 2.2.
– La fonction lireToken() (code non spécifié) sépare les segments du flux de caractères par
rapport à leur type DOM. Ainsi cette fonction doit reconnaître toutes les balises (ouvrantes et
fermantes), mais également distinguer les noms d’attributs et leur valeurs, et ainsi de suite.
– Si le fragment retourné par la fonction lireToken() est une balise fermante, on sort de la
boucle et de la fonction parse() en mettant finNode à true.
– Sinon la fonction getType() (code non spécifié) détermine le type du fragment XML par
sa balise (un fragment sans balise est reconnu comme un nœud de type Text). Ce type permet
ensuite de créer un nœud du même type qui sera ajouté comme fils du nœud père :
père D appendNode(fils)
Si le fils peut avoir lui-même des fils, c’est-à-dire s’il est de type Container, on fait un appel
récursif de la fonction parse() avec le fils comme nouveau père. Sinon le fils est une feuille
et sa valeur est le fragment XML retourné par la fonction extractValue().
Ce parseur est très simplifié et, par exemple, ne prend pas en compte la la DTD du document et les
entités. Néanmoins il devrait permettre une meilleure compréhension du passage XML –> DOM.
L’application de cet algorithme au document précédent est illustrée dans le tableau 2.12. Pendant
l’analyse d’un document XML, les espaces (#20) et retours à la ligne (#xA) sont considérés comme
78 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
des caractères. En effet, la norme du W3C préconise que tous ces « blancs » doivent être transmis sans
modification par le parseur à l’application. L’arbre DOM résultant est représenté dans la figure 2.9.
Document (1)
-
Element (2)
A
Text (3) Element (4) Text (6) Element (7) Text (12)
- B - C -
#xA#x20 #xA#x20 #A
Text (10)
-
#x20
XSLT, mais uniquement du contenu source du document et du programme XSLT. Ainsi, un programme
XSLT suppose par défaut que tous les espaces dans un document (plus précisément dans l’élément racine
de ce document) sont transmis au programme, à l’exception des espaces dans les valeurs d’attributs qui
subissent un traitement spécifiques, et des fins de lignes qui sont normalisées et représentées par le carac-
tère #xA.
Nous n’avons pas représenté, pour des raisons de lisibilité; tous les espaces dans les arbres DOM
jusqu’ici. Nous continuerons à le faire quand les espaces ne sont pas cruciaux pour la représentation du
contenu d’un document ou pour la compréhension du comportement d’un programme.
La fonction child(node, elType) parcourt tous les fils du nœuds node et vérifie avec testN-
ode() que le nœud correspond à un élément de type elType (cette condition est également vérifiée si
elType est égal à « * »).
L’évaluation de cette fonction avec différents nœuds de l’arbre DOM correspondant au fichier ExXML2DOM.xml
est illustrée dans le tableau 2.13. Nous utilisons toujours nos identificateurs pour distinguer entre les dif-
férents nœuds de l’arbre.
La fonction suivante cherche les éléments parmi les descendants de type Element du nœud donné et
utilise la fonction testNode() définie plus haut.
Comme la fonction child(), cette fonction parcourt les fils du nœud donné et sélectionne ceux qui
satisfont le critère de sélection défini par le paramètre elType. Mais elle ne s’arrête pas là, et demande,
par un appel récursif avec le même critère de sélection, les descendants de tous ses fils. L’évaluation de
cette fonction est illustrée dans le tableau 2.14.
L’appel descendant(1,"*"), ou 1 est l’identificateur de la racine du document, retourne tous les
éléments du document XML.
2.4. LE LANGAGE XPATH 81
1. Sélection de nœuds auxquels on souhaite appliquer une règle : dans ce cas l’expression XPath ap-
paraît dans l’attribut select d’une instruction XSLT de type xsl:apply-templates. Par
exemple l’instruction XSLT suivante :
<xsl:apply-templates select="CINEMA/SALLE"/>
va sélectionner tous les nœuds de type SALLE, fils d’un nœud de type CINEMA, lui-même fils du
nœud contexte.
2. Sélection de règles qui doivent être appliquées à des nœuds : l’expression XPath apparaît dans
l’attribut match. Par exemple la règle suivante :
<xsl:template match="FILM/TITRE">
...
</xsl:template>
pourra uniquement être appliqué aux éléments de type TITRE, qui sont des fils d’autres éléments
de type FILM. Seul un sous-ensemble des expressions XPath (les « patterns ») peut être utilisé dans
l’attribut match (voir page 119).
<xsl:value-of select="SALLE/@NO">
extrait la valeur de l’attribut NO d’un élément de type SALLE fils du nœud contexte.
82 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Node
Attribute TreeNode
Leaf Container
Comment Text
CData
Section
Figure 2.10: Sous-ensemble des interfaces DOM pris en compte par XPath
Les arbres DOM restreints aux nœuds connus par XPath seront désignés quand besoin est par le terme
« arbres XPath » dans ce qui suit.
On ne peut donc traiter avec XPath la DTD d’un document XML (un nœud de type DocumentType)
ou des références vers des entités externes (nœuds de type Entity et EntityReference). Concrètement, cela
signifie qu’une application faisant appel à des expressions XPath – par exemple un processeur XSLT – doit
résoudre au préalable les références aux entités et ne pas prendre en compte la DTD, hypothèses que nous
avions déjà envisagées précédemment.
En ce qui concerne les sections littérales (de type CDataSection), les simplifications suivantes sont
effectuées :
1. Le contenu des nœuds CDataSection est transformé en texte ; tout ce passe donc comme si ce type de
nœud était en fait de type Text, avec un contenu transmis tel quel par le parseur. Cette transformation
des sections CDATA en nœuds de type Text est naturelle car l’interface CDataSection est un sous-
type de l’interface Text.
2.4. LE LANGAGE XPATH 83
2. Puis, si plusieurs nœuds textes sont frères et adjacents dans l’arbre DOM, ils sont réunis dans l’arbre
XPath.
La seconde opération vise à satisfaire une règle de base de XML : la séparation d’un texte d’un seul
tenant en deux parties pour constituer deux nœuds distincts n’est pas autorisée. Si on n’opérait pas cette
opération de fusion (dite normalise() dans DOM), l’arbre obtenu après la première simplification ci-dessus
(transformation de CDataSection en Text) serait incorrect.
Prenons un exemple pour illustrer cette simplification. La figure 2.11 montre un arbre DOM avec deux
nœuds frères adjacents. Le nœud de type CDataSection contient des caractères réservés qui n’auraient pas
pu passer tels quels la phase de parsing s’ils n’étaient pas inclus dans une section littérale.
Document
-
Element
EXEMPLE
CDATASection
Text -
- Ceci est une section littérale
Du texte G H
avec des ’&’, des ’ ’ et des ’ ’
Figure 2.11: Un arbre DOM avec deux nœuds de type Text et CDataSection
XPath travaille sur une version simplifiée de cet arbre dans laquelle les deux nœuds ont été fusionnés
(figure 2.12). On constate donc qu’il est possible de trouver des caractères réservés (voire des balises ou
des documents XML entiers) dans les nœuds Text des arbres XPath.
Document
-
Element
EXEMPLE
Text
-
G H
Du texteCeci est une section littérale avec des ’&’, des ’ ’ et des ’ ’
Un deuxième point déjà soulevé déjà plusieurs fois dans ce livre concerne le traitement des espaces dans
un document XML. Nous avons signalé que la recommandation XML préconise la propagation de tous les
espaces blancs dans un document XML à l’application qui traite le document. Voici donc un document
XML simple avec des espaces :
Exemple 26 ExBlanc.xml : Fichier XML avec des espaces
84 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
<?xml version="1.0"
encoding="ISO-8859-1"?>
<?xml-stylesheet href="ex.xsl"
type="text/xsl"?>
En comptant bien, on peut vérifier que ce document contient une trentaine d’espaces qui peuvent être
caractérisés par rapport à leur position dans le document. Ainsi, on trouve des espaces :
5 avant la balise ouvrante de l’élément racine ;
5 à l’intérieur d’une balise ;
5 à l’intérieur d’un élément ;
5 à l’intérieur de la valeur d’un attribut ;
5 entre deux balises ;
5 entre deux attributs.
Une grande partie de ces espaces ne joue pas un rôle significatif pour l’interprétation du contenu d’un
document XML par XPath. Ainsi, il est évident que la plupart des espaces qui apparaissent avant l’élément
racine du document ou dans des nœuds de types DOM qui ne sont pas représentés dans XPath – comme
la déclaration XML ou la DTD – n’ont pas de signification et peuvent être ignorés. De même, certains
espaces servent uniquement comme séparateurs entre des composants syntaxiques XML. C’est vrai par
exemple pour les espaces séparant les attributs d’un élément qui peuvent être « simplifiés » ou remplacés
par un espace simple.
En prenant en compte toutes ces remarques, le document suivant est équivalent au document précédent
du point de vue de XPath :
Après cette première élimination des espaces non-significatifs, XPath considère que le parseur XML a
effectué les transformations suivantes avant de passer les données au processeur XSLT :
Document
-
Instruction Comment
xml-stylesheet - Element
A
href="ex.xsl" type="text/xsl" un commentaire
L’arbre XPath du document résultant est donné dans la figure 2.13. Parmi les espaces qui restent, on
peut distinguer ceux qui se trouvent :
1. entre des éléments ou entre d’autres types de nœuds DOM ;
2. comme unique fils d’un élément (élément de type D), dans un nœud de type Text (" texte ").
Ce sont surtout les deux premiers cas qui sont intéressants, car il représentent des situations ambiguës
concernant l’interprétation des espaces :
1. Dans le cas où un espace apparaît comme nœud de type Text entre deux éléments, on peut souvent
considérer qu’il sert uniquement à augmenter la lisibilité du document.
2. Dans le deuxième cas, la question est de savoir si l’élément doit être considéré comme vide ou pas.
XPath ne permet pas un traitement spécifique de ces cas, mais XSLT fournit les instructions nécessaires
pour rendre ces situations non-ambigues. Une de ces instructions est xsl:xsl:strip-space qui
permet de désigner une liste de types d’éléments dont les fils constitués uniquement d’espace peuvent être
supprimés (strip). Ainsi, par exemple, la commande suivante supprime ce type de nœud pour tous les
éléments du document :
<xsl:strip-space elements="*"/>
En ajoutant cette instruction au début d’une feuille de style, le document précédent devient équivalent
au document suivant, qui ne contient plus de nœuds d’espace (normalement le document contient une seule
ligne que nous avons coupé en deux à cause de la largeur limitée des pages de ce livre) :
L’arbre XPath de ce document est donné dans la figure 2.14. On remarque que l’élément de type C est
maintenant un élément vide et les espaces dans le texte de l’élément D n’ont pas été supprimés.
86 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Document
-
Instruction Comment
xml-stylesheet - Element
A
href="ex.xsl" type="text/xsl" un commentaire
Figure 2.14: Arbre XPath après suppression des nœuds constitués d’espaces.
Document
-
Nous allons développer chacun de ces aspects ci-dessous, en prenant pour exemple principal l’arbre
de la figure 2.15. Comme on peut le constater cet arbre présente à peu près tous les types de nœuds
manipulables par XPath, avec plusieurs configurations. Rappelons que dans nos représentations chaque
nœud est caractérisé par les trois composants suivants :
3. la valeur, valable pour tous les types de nœuds, y compris les attributs, à l’exception du type Ele-
ment.
Cet arbre est obtenu à partir du document sérialisé suivant, que vous pouvez récupérer sur notre site
pour tester les expressions XPath (les outils permettant d’effectuer ces tests sont présentés dans l’annexe A).
<?java ins1?>
<A>
<B att1=’a1’>
<D>Texte1</D>
<D>Texte2</D>
</B>
<B att1=’a2’>
<D>Texte2</D>
</B>
<C att2=’a3’ att3=’15’/>
</A>
<!-- CommFin -->
Chemins XPath
Un chemin peut être absolu, et prendre son origine à la racine du document, notée « / ». Par exemple
l’expression :
/A/B/@att1
correspond à un chemin qui part de la racine du document (« / »), puis passe par l’élément racine si le type
de cet élément est A, parcourt tous les éléments de type B fils de cet élément racine, et enfin désigne les
attributs att1 de ces éléments B. La figure 2.16 montre les deux nœuds, de type Attr, désignés par ces
chemins.
Document
-
Un chemin peut également être relatif et prendre pour origine un nœud du document, dit nœud contexte.
En pratique l’utilisation de chemins relatifs est la plus répandue avec XSLT puisque le principe de base
consiste à déclencher des règles à partir de nœuds considérés comme les nœuds contextes des chemins
XPath. L’expression (noter l’absence du « / » initial) :
A/B/@att1
désigne un chemin qui partant d’un nœud contexte de type quelconque, passe successivement par les nœuds
de type A, B et aboutit aux attributs de nom att1. Ce type d’expression est en principe plus puissant
puisqu’il s’applique à tous les types de documents dans lesquels on retrouve un motif de ce type, quel que
soit par ailleurs la position relative de ce motif par rapport à la racine du document. On peut considérer
un chemin absolu comme une forme particulière de chemin relatif dans laquelle le nœud contexte est
systématiquement la racine du document.
Syntaxe XPath
Un chemin est constitué d’une suite d’étapes, séparées par des « / ». La forme générale est donc :
[/]étape I /étape J /.../étape K
le « / » initial étant optionnel, et distinguant les chemins relatifs des chemins absolus, comme nous l’avons
expliqué précédemment.
Chaque étape peut elle-même se subdiviser en trois composants :
1. l’axe qui définit le sens de parcours des nœuds à partir du nœud contexte ;
2. le filtre qui indique le type des nœuds qui seront retenus dans l’évaluation du chemin ; la notion de
type recouvre ici les types DOM (commentaire, texte, instruction de traitement,...) ainsi que, pour
les éléments et les attributs, le nom de la balise ou de l’attribut ;
3. le (ou les) prédicat(s) qui exprime(nt) des propriétés que doivent satisfaire les nœuds retenus à l’issue
du filtrage pour être finalement inclus dans le résultat.
La forme générale d’une étape est donc :
axe::filtre[prédicat1][prédicat2] :):;:
Signalons dès maintenant qu’il est possible, quand on utilise XPath avec XSLT, d’effectuer une union
de chemins avec l’opérateur « | ». Par exemple l’expression « //A | B/@att1 » désigne l’union des
ensembles désignés par les expressions « //A » et « B/@att1 ».
Les expressions de chemins que nous avons utilisées jusqu’ici correspondaient aux formes les plus
simples du langage XPath. En particulier, comme nous l’avons souligné dans le chapitre 1, nous n’avons
jamais jusqu’ici eu recours aux prédicats qui sont optionnels et nous avons toujours choisi d’utiliser comme
axes de parcours les fils ou les attributs du nœuds contexte. Le premier axe, noté child dans XPath, est
également considéré comme celui par défaut. Le deuxième axe, noté attribute, est distingué par le
symbole « @ » avant le nom de l’attribut. On peut noter que les attributs sont traités par un axe spécifique.
Document
-
À partir de la racine, on évalue donc l’étape 1. Le filtre est A : on recherche un nœud fils, dont le type
DOM est Element, et le nom de balise A. Si on le trouve, il ne peut y en avoir qu’un seul car c’est l’élément
racine du document : le résultat de l’étape 1 est donc un ensemble réduit à un seul nœud (figure 2.18).
Maintenant on prend ce nœud comme contexte, et on évalue l’étape 2 dont le filtre est B. Le résultat
de cette évaluation va être l’ensemble des nœuds dont le type DOM est Element, et le nom de balise B
(figure 2.19).
Enfin, on va prendre chaque nœud de type B comme nœud contexte, et on cherchera pour chacun un
attribut att1 : le résultat final est un ensemble d’attributs, autrement dit de nœuds dont le type DOM est
Attr. On obtient le résultat de la figure 2.16, page 87.
L’évaluation du chemin /A/B/@att1 peut également être illustré par un programme qui utilise la
fonction child(Node node, DOMString elType). Cette fonction prenait comme paramètre un
nœud – le nœud contexte – et un nom d’élément. Le résultat était l’ensemble des fils de type elType du
nœud contexte. Voici un fragment de programme qui évalue le chemin /A/B/@att1 (on suppose que
doc correspond au nœud racine du document) :
90 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Document
-
Document
-
Contexte d’évaluation
Chaque étape d’une expression XPath est évaluée en fonction d’un ensemble d’informations qui constituent
le contexte de l’évaluation. Il est très important d’être conscient que le contexte change à chaque étape,
d’une part pour comprendre le résultat d’une évaluation XPath, d’autre part pour construire correctement
des expressions.
Ce contexte comprend en premier lieu le nœud que nous avons appelé nœud contexte jusqu’à présent,
à partir duquel sont construits les chemins des étapes suivantes. Mais le contexte d’évaluation d’une étape
comprend également l’ensemble de nœuds obtenu par l’évaluation de l’étape précédente, constituant le
contexte commun d’évaluation pour chaque nœud pris individuellement.
Reprenons l’exemple précédent pour clarifier cette notion de contexte. L’expression XPath est
/A/B/@att1
avec trois étapes. À l’issue de la seconde étape, correspondant au filtre B, on obtient un ensemble com-
portant deux nœuds, représenté dans la figure 2.19. Cet ensemble va constituer le contexte commun à
l’évaluation de l’étape suivante, @att1.
Les nœuds de cet ensemble sont alors considérés chacun leur tour en tant que nœud contexte. L’évaluation
de l’étape @att1 s’effectue autant de fois qu’il y a de nœuds contextes, en tenant compte du contexte com-
mun. En pratique, on pourra connaître la position d’un nœud dans son contexte (fonction position()) ou
savoir si un nœud est le dernier de son contexte (fonction last()). Ici il y a deux positions, 1 et 2, carac-
térisant les deux occurrences de nœuds de type B.
Le contexte d’évaluation d’une étape dans une expression XPath comprend également des variables,
des fonctions et des espaces nominaux, aspects complémentaires sur lesquels nous reviendrons ultérieure-
ment.
Axe Description
child Les fils du nœud contexte
attribute Les attributs du nœud contexte
parent Le père du nœud contexte
descendant Tous les descendants du nœud contexte
ancestor Tous les ancêtres du nœud contexte
self Le nœud contexte lui-même
preceding-sibling Tous les frères gauches du nœud contexte
following-sibling Tous les frères droits du nœud contexte
preceding Les nœuds précédant le nœud contexte dans l’ordre de parcours du doc-
ument
following Les nœuds suivant le nœud contexte dans l’ordre de parcours du docu-
ment
descendant-or-self Les descendants du nœud contexte, et le nœud contexte lui-même
ancestor-or-self Les ancêtres du nœud contexte, et le nœud contexte lui-même
namespace S’applique aux espace de noms : voir chapitre 6
Document
-
L’axe child
L’axe child désigne les fils du nœud contexte. Cet axe ne s’applique pas aux attributs, mais prend en
compte en revanche tous les autres types de nœuds DOM. La figure 2.21 montre l’unique nœud désigné
par cet axe à partir de notre nœud contexte.
Document
-
Element Element
B Nœud contexte C
L’axe attribute
Cet axe constitue une exception car il est le seul à s’appliquer à l’arbre des attributs. Cet arbre est lui-même
très particulier puisqu’il a toujours pour racine un nœud de type Element, et un seul niveau constitué de
feuilles, non ordonnées mais identifiées par leur nom, les attributs.
Document
-
Element Element
B Nœud contexte C
En conséquence, l’axe sera toujours utilisé dans la dernière étape d’une expression XPath, et l’attribut
sera désigné par son nom. L’étape permettant de sélectionner l’attribut de nom att1 à partir de notre nœud
contexte (figure 2.22) est donc :
attribute::att1
On utilise le plus souvent l’abréviation @nom pour remplacer attribute::nom (remarquez la dis-
parition des ::). On notera donc l’étape précédente de manière équivalente :
@att1
94 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
L’ensemble des attributs de nom att1 associés à un élément de type B lui-même fils d’un élément-
racine de type A est donc désigné par l’expression :
/A/B/@att1
L’axe parent
Cet axe désigne le nœud père du nœud contexte (figure 2.23). L’étape suivante permet donc de « remonter »
d’un niveau dans l’arbre à partir de notre nœud de type B :
parent::A
Document
-
Element Element
B Nœud contexte C
L’abréviation pour cet axe est « .. », conformément à une convention largement utilisée dans des sys-
tèmes de fichiers hiérarchique sous Unix ou DOS. En fait cette abréviation n’est pas strictement équivalente
à parent::A qui utilise le filtre A alors que « .. » désigne le nœud père quel que soit son type. L’équivalent
de « .. » en notation développée est :
parent::node()
où node() est un filtre qui sélectionne tous les types de nœuds de l’arbre DOM (à l’exception des at-
tributs).
On peut composer cet axe pour remonter d’un nombre donné de niveaux dans l’arbre. Par exemple
l’expression suivante :
../..
désigne, à partir de notre nœud contexte de référence, la racine du document. On pourrait à partir de là
redescendre sur l’un des trois fils de la racine, mais on rencontre alors un problème de nommage puisque
seul l’un de ces fils est de type Element avec un nom. Nous verrons page 98 comment utiliser des modes
de référencement alternatifs au nommage des nœuds. On peut utiliser node() pour éviter ce problème de
nommage :
../../node()
Cette expression désigne donc les trois fils de la racine du document, qui sont de trois types différents.
Notez bien que cette expression est une abréviation de
parent::node()/parent::node()/child::node()
où le dernier axe child indique bien que les attributs – s’il y en avait – ne sont pas concernés.
2.4. LE LANGAGE XPATH 95
L’axe descendant
Cet axe permet de désigner tous les nœuds de l’arbre DOM principal (à l’exception donc des attributs et
des espaces de nom) qui sont descendants du nœud contexte. Voici l’expression donnant les descendants
de notre nœud contexte, tous types confondus (figure 2.24) :
descendant::node()
Document
-
Element Element
B Nœud contexte C
Le filtre node() est ici important pour désigner à la fois un nœud de type Element et un nœud de type
Text. Le premier, seul, serait obtenu par :
descendant::D
et le second par
descendant::text()
L’axe ancestor
L’axe ancestor est réciproque de descendant : il désigne tous les ancêtres du nœud contexte, et ce
quel que soit leur type (toujours à l’exception du type Attr). La figure 2.25 montre, sur notre exemple de
référence, les nœuds désignés par
ancestor::node()
L’axe self
Cet axe désigne le nœud contexte lui-même. Le résultat dépend ensuite du filtre qui est appliqué. Sur notre
exemple, l’expression suivante :
self::B
revient bien à sélectionner le nœud de type B qui constitue notre nœud contexte. En revanche
self::A
96 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Document
-
Element Element
B Nœud contexte C
renverrait, toujours sur notre exemple, un ensemble vide puisque le nœud contexte n’est pas de type A.
Afin de pouvoir désigner le nœud contexte sans avoir à s’embarasser de donner son type, on utilise souvent
l’expression
self::node()
qui utilise le filtre node(), valide pour tout type de nœud (sauf les attributs !). Il existe une notation abrégée,
« . », pour self::node(), très fréquemment utilisée. L’expression suivante utilise donc des notations
abrégée pour désigner l’attribut att1 du nœud contexte :
./@att1
Sans utiliser la notation abrégée, cette expression s’écrirait :
self::node()/attribute::att1
Ces deux axes désignent respectivement les frères à gauche et à droite du nœud contexte. La figure 2.26
montre l’axe preceding-sibling appliqué à notre exemple, avec un filtre node().
preceding-sibling::node()
On obtient évidemment en l’occurrence le même résultat avec l’expression suivante, dont le filtre limite
la sélection des nœuds aux éléments de type B :
preceding-sibling::B
La figure 2.27 montre sur notre exemple les nœuds sélectionnés par l’expression :
following::node()
2.4. LE LANGAGE XPATH 97
Document
-
Document
-
descendant-or-self::node()
désigne les mêmes nœuds que ceux déjà montrés dans la figure 2.24, avec le nœud contexte en plus.
/A/B/D
désigne, en partant de la racine du document, l’élément de nom A, puis les éléments de nom B fils de A,
et enfin les éléments de nom D fils de B. Les chemins obtenus sur notre exemple sont montrés dans la
figure 2.28.
Document
-
Document
-
Un filtrage avec nom d’attributs ne peut intervenir que dans la dernière étape d’une expression XPath
(puisqu’un attribut est toujours un nœud feuille). Voici par exemple une expression qui désigne tous les
attributs nommés att2, quelle que soit par ailleurs leur position dans l’arbre. On combine une étape
/descendant::node() qui, à partir de la racine, va sélectionner tous les nœuds à l’exception des
attributs, puis pour chacun de ces nœuds on regarde s’il existe un attribut att2.
/descendant::node()/@att2
/descendant::node()/@*
De même l’expression suivante sélectionne tous les éléments fils de l’élément racine F AE , quel que
soit leur nom :
/A/*
Le résultat est donné dans la figure 2.30. Notez que seuls les éléments de type Element sont concernés,
et pas ceux de type Comment, Text (qui n’ont pas de nom) ou ProcessingInstruction (qui peuvent être
filtré différemment).
Document
-
Filtre Description
text() Désigne les nœuds de type Text
comment() Désigne les nœuds de type Comment
processing-instruction() Désigne les nœuds de type ProcessingInstruction
node() Désigne tous les types de nœud
/comment()
L’expression ci-dessus désigne tous les commentaires fils de la racine, sans qu’il soit possible d’être
plus précis puisqu’un commentaire n’a pas de caractère distinctif. En revanche on peut faire référence de
manière plus précise à une instruction de traitement en utilisant son nom :
/processing-instruction(’java’)
Enfin le filtre text() désigne les nœuds de type Text. Voici l’exemple d’une expression désignant
tous les nœuds de texte situés sous un élément F B E
/A/B//text()
/A/B/descendant-or-self::node()/text()
Le résultat de l’expression appliquée à notre exemple de référence est donné dans la figure 2.31.
2.4.5 Prédicats
La troisième partie d’une étape est un prédicat, autrement dit une expression booléenne constituée d’un ou
plusieurs tests, composés avec les connecteurs logiques habituels and et or (la négation est fournie sous
la forme d’une fonction not()).
Un test est toute expression donnant un résultat booléen (true ou false). En fait à peu près n’importe
quelle expression peut être convertie en un booléen avec XPath, ce qui donne beaucoup de liberté, au prix
parfois d’une certaine obscurité.
2.4. LE LANGAGE XPATH 101
Document
-
Un ensemble de nœuds par exemple est interprété comme true s’il n’est pas vide, et comme false
sinon. L’expression
/A/B[@att1]
a donc la signification suivante : le prédicat est l’ensemble des attributs de nom att1 des éléments de type
B. S’il n’y a pas d’attribut att1 l’ensemble est vide, donc il a pour valeur booléenne false. Pour dire
les choses simplement, cette expression sélectionne les éléments de type B qui ont un attribut att1.
Les tests peuvent également consister à exprimer des conditions sur les valeurs de certains nœuds ou
variables, et à appeler des fonctions parmi celles proposées par XPath.
Prédicats simples
Les fonctions les plus utilisées sont probablement position() qui donne la position du nœud au sein de
contexte, et last() qui donne la position du dernier nœud dans le contexte (autrement dit le nombre de
nœuds dans le contexte puisque les positions sont numérotées à partir de 1). Voici un premier exemple
d’une expression XPath avec prédicat.
/A/B/descendant::text()[position()=1]
Le résultat est celui de la figure 2.32 : on obtient les premiers nœuds de type Text descendant de
chacun des deux nœuds désignés par /A/B. Il est très important de noter que le prédicat d’une étape prend
en compte le contexte constitué de l’ensemble de nœuds obtenu par évaluation de l’axe et du filtre de cette
même étape. Ici l’étape est évaluée deux fois, pour chacun des nœuds de type B, et à chaque fois le prédicat
est appliqué aux nœuds obtenus par évaluation de descendant::text().
Revenons sur les fonctions position() et last(). Elle s’appuient sur une numérotation des nœuds dans
un ensemble, elle-même fonction de deux critères :
Document
-
Il est possible de tester les valeurs de certains nœuds en faisant référence à ces nœuds par des expres-
sions XPath. Voici un premier exemple simple : une expression qui sélectionne tous les éléments de nom
B ayant un attribut att1 dont la valeur est a1.
/A/B[@att1=’a1’]
Les prédicats peuvent ainsi référencer des nœuds très éloignés du nœud contexte. Voici par exemple
une expression qui sélectionne tous les nœuds de nom B si et seulement si il existe un nœud fils de l’élément
racine donc le type est C avec un attribut att3 ayant la valeur 15.
/A/B[/A/C/@att3=15]
On peut s’attendre à ce que cette expression soit très coûteuse à évaluer puisqu’il faudra, pour chaque
nœud B, tester le prédicat en suivant le chemin dans l’arbre DOM (à moins d’utiliser un processeur XPath
assez malin pour s’apercevoir que le résultat du prédicat est indépendant du contexte et peut donc s’évaluer
une seule fois).
On peut effectuer des compositions booléennes des conditions élémentaires avec les connecteurs and
et or (le not est une fonction not() dans XPath). Voici une expression qui prend le dernier élément B fils
de l’élément racine A, et ce seulement si ce dernier élément a un attribut att1 dont la valeur est a1.
Composition de prédicats
L’utilisation d’une succession de prédicats marqués par les crochets [ ] dénote une composition. La dif-
férence essentielle d’une telle composition, par rapport à l’utilisation de connecteurs logiques and et
or, est que l’ensemble de nœuds issus de l’évaluation du premier prédicat est pris comme contexte pour
l’évaluation du second, et ainsi de suite. Reprenons l’exemple précédent pour clarifier les choses.
On pourrait très bien dans cet exemple inverser les deux termes du and sans changer la signification de
l’expression : le prédicat s’évalue dans les deux cas en prenant pour contexte l’ensemble des nœuds obtenu
à l’issue de A/B. Prenons maintenant l’expression suivante :
/A/B[@att1=’a1’][position()=last()]
2.4. LE LANGAGE XPATH 103
Ici on va pendre tous les éléments B petit-fils de la racine du document qui ont un attribut att1 valant
a1 (premier prédicat). Puis on évalue le second prédicat pour ne garder que le dernier parmi les nœuds qui
ont satisfait le premier prédicat. Si on inverse les deux prédicats on a l’expression :
/A/B[position()=last()][@att1=’a1’]
Ici on commence par prendre le dernier nœud de type B, et on ne le garde que s’il a un attribut att1
de type a1. On obtient donc un résultat différent.
1. les numériques utilisent la notation décimale habituelle, avec un point pour les flottants (10.5) et un
signe moins (-) devant les nombres négatifs (-10) ;
2. les chaînes de caractères sont constituées d’une suite de caractères reconnus par le codage du docu-
ment, et délimitées par des guillemets (") ou des apostrophes (’) : attention les blancs sont significatifs
(“azerty” est différent de “ azerty “) ;
4. enfin les ensembles de nœuds sont un sous-ensemble, sans doublon, des nœuds du document source.
Les comparateurs usuels ( F , E , !=, =, F+L , E+L ) peuvent être utilisés pour tous les types scalaires
(numériques, chaînes et booléens). Pour les opérations on peut avoir besoin d’effectuer une conversion
d’un type à un autre. Ces conversions peuvent être soit implicites, quand l’expression indique clairement
que l’une des opérandes appartient à un type donné, soit explicites avec les fonctions number(), boolean()
et string().
Il faut bien noter qu’un document XML est essentiellement une chaîne de caractères structurée par des
balises, et n’est donc pas typé, comme peut l’être un programme écrit dans un langage comme Java ou
C++, chaque variable y étant déclarée comme appartenant à un certain type. Il ne peut donc y avoir de
garantie que le résultat d’une expression XPath puisse être converti dans le type attendu, et ce même si le
document est valide (conforme à une DTD). Cependant toute expression peut être convertie (« interprétée »)
soit comme un booléen, soit comme une chaîne, ce qui est particulièrement important pour les raisons
suivantes :
1. les tests effectués dans XSLT (xsl:if, xsl:when) peuvent prendre n’importe quelle expression,
l’évaluer, et considérer le résultat comme true ou false ;
2. la production de texte en XSLT (balise xsl:value-of) doit convertir le résultat d’une expression
XPath en chaîne de caractères pour l’incorporer au document résultat.
Les numériques
Les opérateurs sur les types numériques sont donnés dans le tableau 2.17. La valeur numérique d’un nœud
est obtenue en lui appliquant la fonction number() qui fonctionne de la manière suivante :
5 élimination des blancs avant le premier caractère et après le dernier caractère ;
5 recherche du signe ’-’ ;
5 interprétation de la chaîne restante comme un numérique.
Si la conversion est impossible, la fonction renvoie NaN (« Not a Number »). Voici par exemple une
expression XPath qui recherche tous les nœuds ayant un attribut dont la valeur est impaire.
//node()[number(@att1) mod 2 = 1]
Sur notre exemple de référence, on obtiendrait le nœud C (figure 2.33).
Document
-
Pour tous les nœuds ayant un attribut dont la valeur est une chaîne de caractères qui ne peut être
convertie vers un numérique, le résultat de la fonction est NaN,
Les comparaisons avec un NaN donnent des résultats assez étranges. Un test d’égalité renvoie false
si l’une des opérandes est NaN. En revanche un test d’inégalité (« != ») renvoie toujours true quand une
des opérandes est NaN. Voici quelques conséquences que nous vous laissons méditer :
2.4. LE LANGAGE XPATH 105
Les booléens
Les booléens prennent deux valeurs, true et false, que l’on peut explicitement introduire dans les
expressions avec les fonctions true() et false(). Toutes les chaînes avec au moins un caractère, ainsi que
tous les numériques non nuls, sont assimilés à la valeur booléenne true ; les chaînes vides, la valeur 0
ou NaN sont assimilés à false. Ces règles permettent d’interpréter (en se donnant un peu de mal) les
conversions effectuées depuis une valeur de type numérique ou chaîne de caractères vers un booléen, soit
implicitement, soit avec la fonction boolean().
La conversion d’un booléen vers un autre type scalaire s’effectue selon les règles suivantes :
5 Vers un numérique (fonction number()) : on obtient 1 avec true, et 0 avec false.
5 Vers une chaîne (fonction string()) : on obtient la chaîne false ou la chaîne true.
Voici quelques exemples :
1. boolean(’azerty’) = true
2. boolean(’0’) = true
3. boolean(0) = false
4. number (1 = 0) = 0
5. number (1 = 1) = 1
6. string (1 = 1) = ’true’
Les choses se compliquent un peu plus quand on doit effectuer une comparaison entre un booléen et
une autre valeur, et notamment quand la conversion des deux opérandes est nécessaire pour les ramener
toutes deux au même type. Voici les règles de base :
1. quand l’opérateur est « = » ou « != » (comparison operator) la valeur non booléenne est convertie en
booléen ;
2. pour les autres opérateurs, on tient compte du type de l’opérande non booléenne :
(a) si c’est un numérique, on convertit le booléen en numérique (donc 0 ou 1) ;
(b) si c’est une chaîne, on convertit les deux opérandes en numérique.
Voici quelques exemples :
1. true() = 5 : on convertit 5 en booléen, ce qui donne true, et le résultat de la comparaison est
true ;
2. true() E 5 : on convertit le booléen true en numérique, ce qui donne 1, et le résultat de la
comparaison est false ;
3. true() != ’azerty’ : on convertit azerty en booléen, ce qui donne true, et le résultat de
la comparaison est false ;
4. false() E ’azerty’ : on convertit le booléen false en numérique, ce qui donne 0, ainsi que
azerty, ce qui donne 1, et le résultat de la comparaison est false ;
106 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Ensembles de nœuds
Étudions pour finir le cas des ensembles de nœuds. Notons tout d’abord qu’il est impossible de convertir
un type scalaire vers un ensemble de nœuds, ce qui divise par deux le problème : les conversions seront
toujours appliquées à un ensemble pour le transformer vers un type scalaire.
La conversion la plus courante s’effectue vers une chaîne de caractères. Voici tout d’abord les règles
donnant une valeur textuelle pour chaque type de nœud d’un arbre XPath :
5 pour un nœud de type Text, on obtient directement le contenu du nœud ;
5 pour un nœud de type Comment, on obtient toute la chaîne comprise entre ’ F !--’ et ’-- E ’ ;
5 pour un nœud de type ProcessingInstruction, on obtient toute la chaîne comprise entre le nom (ou
« cible ») de l’instruction et ’? E ’ ;
5 pour un nœud de type Attr, on obtient la valeur de l’attribut ;
5 enfin pour un nœud de type Element ou de type Document, le contenu textuel est la concaténation
de tous les contenus des descendants du nœud qui sont de type Text.
Par exemple, en reprenant encore une fois notre exemple de référence, le contenu de l’élément /A est la
concaténation de Texte1, Texte2 et Texte3 ; le contenu de l’élément /A/B[1]5 est la concaténation
de Texte1, Texte2, etc.
On peut maintenant définir comme suit les conversions d’un ensemble de nœuds vers les types scalaires :
1. vers une chaîne de caractères : c’est le contenu textuel du nœud de l’ensemble qui apparaît en
première position dans l’ordre du document.
2. vers un numérique : on convertit d’abord en chaîne, puis on convertit la chaîne avec number() ;
3. vers un booléen : la conversion booléenne d’un ensemble de nœuds donne true si l’ensemble n’est
pas vide, false sinon.
Le tableau 2.18 résume les règles de conversion que nous avons vues jusqu’ici.
Il est maintenant possible d’effectuer des comparaisons entre un ensemble de nœuds et une instance de
n’importe quel autre type XPath. Voici les règles à appliquer à un ensemble de nœuds dénoté O :
1. pour comparer O et une chaîne P , on convertit tous les nœuds de O vers leurs valeurs textuelles ; si
une seule de ces valeurs est égale à P la comparaison renvoie true, et false sinon ;
2. pour comparer O et un nombre Q , on effectue comme précédemment une conversion de chaque
nœuds vers une valeur numérique, et on compare avec Q ;
5 Cette expression est équivalent à /A/B[position() = 1].
2.4. LE LANGAGE XPATH 107
3. pour une comparaison avec une valeur booléenne, on prend simplement la conversion de O vers un
booléen ;
4. enfin pour comparer O avec un second ensemble ORJ , on prend les valeurs textuelles des nœuds de
O et de ORJ et on les compare deux à deux : si on obtient true pour au moins une paire, alors la
comparaison globale renvoie true également.
Le tableau 2.19 récapitule les abréviations des expressions XPath. Il faut être prudent dans l’utilisation
de ces abréviations, et toujours se ramener à la forme développée quand on n’est pas sûr de la signification
de la forme abrégée. Si vous n’êtes pas convaincus, nous vous suggérons par exemple le lecteur de réfléchir
à la différence entre //B[1] et de /descendant::B[1]...
L’annexe B complète la description de XPath qui précède, en donnant notamment une liste complète
des fonctions reconnues par la norme.
108 CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Abréviation Description
. Équivalent à self::node()
.. Équivalent à parent::node()
// Équivalent à /descendant-or-self::node()/
.// Équivalent à self::node()/descendant-or-self::node()/
@att Équivalent à attribute::att
XSLT
Sommaire
3.1 Programmes XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
3.1.1 Structure d’un programme XSLT . . . . . . . . . . . . . . . . . . . . . . . . . 111
3.1.2 Modularité : xsl:import et xsl:include . . . . . . . . . . . . . . . . . 115
3.1.3 Application d’un programme XSLT . . . . . . . . . . . . . . . . . . . . . . . . 117
3.2 Les règles XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
3.2.1 Les patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
3.2.2 Règles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
3.2.3 Déclenchement de règles avec xsl:apply-templates . . . . . . . . . . . 124
3.2.4 Sélection des règles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
3.2.5 Appel de règle avec xsl:call-template . . . . . . . . . . . . . . . . . . 131
3.2.6 Paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
3.3 Instructions de contrôle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
3.3.1 Tests : xsl:if et xsl:choose . . . . . . . . . . . . . . . . . . . . . . . . . 134
3.3.2 Boucles : xsl:for-each . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
3.3.3 Variables : xsl:variable . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
3.3.4 Tri : xsl:sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
3.3.5 Itérations par récursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
3.4 Évaluation d’un programme XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
Dans ce chapitre nous commençons notre étude approfondie de XSLT par les aspects relatifs à la pro-
grammation. Nous rassemblons sous ce terme toutes les instructions qui permettent de spécifier une trans-
formation d’un document source vers un document résultat. Il n’est sans doute pas inutile d’expliciter les
parties de XSLT qui ne relèvent pas directement de cette notion de programmation, et qui seront abordées
plus loin dans ce livre :
2. les spécificités du langage de sortie sont pour l’instant ignorées : les exemples seront basés sur la
production de documents HTML simples ;
3. enfin nous délaissons pour l’instant les instructions qui sont orientées vers la mise en forme du
document résultat.
Si vous avez lu les chapitres qui précèdent – ce qui est recommandé – vous devriez déjà avoir une
compréhension intuitive d’une part des principes de base de XSLT, et d’autre part de la structuration d’un
document XML. Nous nous plaçons donc directement dans la situation où un programme, le processeur
XSLT, dispose en entrée de deux documents (figure 3.1) :
109
110 CHAPTER 3. XSLT
Processeur
arbre arbre
XSLT
DOM résultat
Sérialisation
Analyse
Nous supposons que le document source a été analysé et se trouve disponible pour le processeur sous
forme d’un arbre DOM. Notez bien qu’il s’agit d’une vue « conceptuelle » et que, en pratique, les pro-
cesseurs XSLT sont libres de s’appuyer sur une représentation interne de leur choix. Sur la base des
informations trouvées dans le document source et des instructions du programme XSLT, le processeur pro-
duit un document résultat, qui peut également être vu comme un arbre construit au fur et à mesure de
l’évaluation (nous donnerons un exemple d’une telle évaluation en fin de chapitre). Une fois la transfor-
mation terminée, l’arbre résultat peut être sérialisé si besoin est pour être stocké, transmis sur le réseau,
etc
Nous prendrons comme exemple principal dans ce chapitre le document suivant, décrivant un cours (ou
l’ébauche d’un cours..) basé sur le présent livre.
<COURS CODE="TC234">
<SUJET>Publication XSLT</SUJET>
<ENSEIGNANTS>
<!-- Enseignant responsable -->
<NOM>Amann</NOM>
<NOM>Rigaux</NOM>
</ENSEIGNANTS>
<PROGRAMME>
<SEANCE ID="1">Documents XML</SEANCE>
<SEANCE ID="2">Programmation XSLT</SEANCE>
<ANNEE>2002</ANNEE>
</PROGRAMME>
</COURS>
Nous commençons par décrire de manière globale le contenu d’un programme XSLT et des différents
types d’éléments que l’on peut y trouver. Nous passons ensuite à la description des règles (template), puis
aux instructions de contrôle qui permettent d’effectuer des itérations et tests. Enfin nous concluons ce
chapitre par l’exemple détaillé d’une évaluation d’un traitement XSLT.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="COURS">
<html>
<head><title>Fiche du cours</title></head>
<body bgcolor="white">
<p>
<h1>
<i><xsl:value-of select="SUJET"/></i>
</h1>
<hr>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Les éléments XSLT ne posent pas de problème, mais l’unique règle tente de produire le résultat suivant :
<html>
<head><title>Fiche du cours</title></head>
<body bgcolor="white">
<p>
<h1>
<i>Publication XSLT</i>
</h1>
<hr>
</body>
</html>
Bien qu’il s’agisse d’un document HTML très correct (en tout cas conforme à la norme HTML 4.0),
les éléments littéraux T p U et T hr U ne sont pas refermés. La présence de ce fragment HTML n’est donc
pas acceptée dans le programme XSLT.
Cela illustre bien le fait que XSLT est avant tout destiné à transformer un document XML en un
autre document XML, par production de fragments bien formés. La sortie d’un document « texte » qui
ne serait pas du XML bien formé est possible mais implique des options spéciales qui seront étudiées dans
le chapitre 4.
L’élément xsl:stylesheet
L’élément racine d’un document XSLT est de type xsl:stylesheet. La forme habituelle de cet élé-
ment est :
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
112 CHAPTER 3. XSLT
Il existe d’autres attributs possibles pour xsl:stylesheet. Ils seront présentés au fur et à mesure.
Notez qu’il existe également un type d’élément xsl:transform qui est l’exact synonyme de xsl:stylesheet.
Remarque : Dans ce chapitre et les suivants nous avons choisi de ne pas donner une présentation ex-
haustive des attributs et des règles syntaxiques relatives à chaque élément XSLT, afin de rendre la lecture
plus facile et de privilégier la « mise en contexte » de ces éléments. L’annexe B en revanche contient une
référence XSLT avec la syntaxe de chaque élément : l’élément xsl:stylesheet par exemple est décrit
page 278.
Sous l’élément racine xsl:stylesheet on trouve le contenu du programme au sein duquel on dis-
tingue communément deux types d’éléments. Tout d’abord les éléments de premier niveau (top-level ele-
ments) sont fils de l’élément racine xsl:stylesheet. Le principal élément de ce type est xsl:template
qui est utilisé pour définir les règles XSLT. Les instructions constituent le second type d’élément : on les
trouve essentiellement dans les corps de règles.
Tous ces types d’élément ne sont pas de la même importance, et tous ne couvrent pas le même type
de fonctionnalité. Même s’il est difficile d’établir une classification qui ne soit pas, au moins en partie,
arbitraire, on peut grossièrement distinguer
S les éléments qui affectent la production du document résultat ;
3.1. PROGRAMMES XSLT 113
Dans ce chapitre nous décrivons les éléments qui relèvent de la seconde catégorie : xsl:template,
xsl:param et xsl:variable. Les éléments relatifs à la production du résultat sont pour l’essentiel
présentés dans le prochaine chapitre.
L’ordre des éléments de premier niveau n’a pas d’impact sur le comportement du programme XSLT
(à l’exception de xsl:import : voir ci-dessous), et on peut les arranger dans l’ordre qui semble le plus
convenable pour la lecture. Cette particularité constitue l’un des aspects du caractère « déclaratif » de
XSLT : on indique les actions à effectuer en présence de certains événements, à charge pour le processeur
de déclencher ces actions de manière appropriée.
Quels sont les autres fils possibles de xsl:stylesheet ? Il est interdit d’avoir des nœuds de type
Text, autrement dit du texte placé immédiatement sous l’élément racine. En revanche on peut trouver des
commentaires et des instructions de traitement, les premiers étant ignorés, et le second pris en compte
seulement s’ils contiennent des instructions reconnues par le processeur.
Dans le même esprit, on peut trouver au premier niveau des éléments dont le type est spécifique au
processeur XSLT. En général ces éléments se distinguent par un préfixe comme xalan:, msxml3: ou
saxon:. L’introduction de ce type d’élément non normalisé risque de poser des problèmes de portabilité
si on souhaite qu’un programme XSLT puisse être traité par n’importe quel processeur.
Enfin le concepteur d’un programme peut lui-même définir ses propres éléments de premier niveau,
en les caractérisant par un préfixe. Cela n’a d’intérêt que pour introduire du contenu (par exemple : des
codifications, des messages en plusieurs langues, des paramètres..) au sein d’un programme, en plus des
instructions de ce programme. Ces éléments sont ignorés par le processeur, et leur utilité est douteuse. On
peut en tout cas toujours s’en passer et utiliser d’autres mécanismes.
En résumé les éléments du tableau 3.1 constituent la référence des types d’éléments, fils de xsl:stylesheet,
reconnus par n’importe quel processeur XSLT conforme à la recommandation XSLT 1.0. Un des ajouts
importants prévus dans la version 1.1 est un élément xsl:script qui permettra d’introduire des parties
programmées (en Java ou JavaScript) dans un programme XSLT. Cette extension risque de soulever des
problèmes de portabilité et fait l’objet d’un débat au sein du groupe de normalisation.
Instructions XSLT
L’exécution d’un programme XSLT consiste à déclencher (ou instancier) des règles définies par des élé-
ments de premier niveau xsl:template. L’instanciation d’une règle consiste à produire un fragment du
document résultat en fonction :
1. d’éléments littéraux ;
2. de texte ;
3. d’instructions XSLT.
Toute combinaison de tels constituants est nommée un corps de règle. Alors que les deux premières
catégories (éléments littéraux et texte) sont insérés dans le document résultat, les instructions XSLT sont
interprétées par le processeur XSLT. Voici par exemple un corps de règle que nous avons rencontré dans le
chapitre introductif : il crée une liste HTML avec les séances d’une salle de cinéma :
<h3>Séances</h3><ol>
<xsl:for-each select="SEANCES/SEANCE">
<li><xsl:value-of select="."/></li>
</xsl:for-each>
</ol>
On trouve donc des éléments littéraux (balises T h3 U , T ol U , T li U et leurs fermetures), des nœuds
de type Text (par exemple le nœud fils de T h3 U dont le contenu est Séances), et enfin des instructions,
éléments dont le nom est préfixé par xsl:. La notion de corps de règle est assez large et recouvre toute
partie du programme destinée à produire un fragment du document résultat.
114 CHAPTER 3. XSLT
Le tableau 3.2 donne la liste de toutes les instructions XSLT 1.0 à l’exception de xsl:document qui
est défini dans XSLT 1.1, mais déjà largement présent dans les implantations actuelles. Comme pour les
éléments de premier niveau, nous donnons une brève description, ainsi que la page de l’annexe B où vous
pouvez trouver la référence de l’instruction.
On peut noter que xsl:variable et xsl:param sont les types d’élément à apparaître à la fois
comme élément de premier niveau et comme instruction. L’emplacement de l’élément définit la portée de
la variable et du paramètre : voir page 138.
Si l’on s’en tient à la classification déjà faite précédemment, on constate que les instructions rela-
tives à la transformation du document source plutôt qu’à la production du résultat sont xsl:apply-
templates et xsl:call-template qui toutes deux déclenchent des règles, xsl:choose, xsl:for-
each, xsl:if, xsl:message et xsl:variable qui correspondent à peu près aux tests, itérations,
sorties à l’écran et définitions de variables que l’on trouve dans la programmation la plus classique. Ces
« instructions de contrôle » sont présentées page 134
3.1. PROGRAMMES XSLT 115
1. dans le cas de xsl:import le processeur affecte aux règles importées une préséance1 inférieure à
celles du programme importateur ;
2. dans le cas de xsl:include, le processeur traite les règles du programme inclus sans les dis-
tinguer, en terme de préséance, de celles du programme principal : tout se passe comme si xsl:include
était simplement remplacé, avant l’évaluation, par le contenu du programme référencé.
Une autre différence entre ces deux types d’élément est que xsl:import doit apparaître avant
tout autre élément de premier niveau dans un programme XSLT. On peut trouver en revanche plusieurs
xsl:import puisque rien n’empêche d’importer plusieurs fichiers dans un programme.
À l’exception des différences ci-dessus, xsl:include et xsl:import sont très proches. Dans les
deux cas un fichier inclus/importé peut lui-même inclure ou importer d’autres fichiers, tant qu’il n’existe
pas de cycle qui mènerait un fichier à s’inclure lui-même. Les deux types d’élément partagent également
la même syntaxe : le nom ou l’URI du fichier inclus est référencé dans l’attribut href :
<xsl:import href="Programme.xsl"/>
Prenons un premier exemple pour illustrer l’intérêt du mécanisme (nous utilisons xsl:import mais
on pourrait dans l’exemple ci-dessous remplacer xsl:import par xsl:include avec un résultat
équivalent – il n’y a pas de conflit de règles).
Le fichier ExXSLTImport.xsl contient une première règle qui s’applique à un élément racine de type
COURS, produit un « squelette » de document HTML avec le sujet du cours dans la balise T TITRE U , et
enfin déclenche un appel de règles avec xsl:apply-templates.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<html>
<head><title><xsl:value-of select="COURS/SUJET"/></title></head>
<body bgcolor="white">
<xsl:apply-templates/>
1 Nous utilisons le terme (un peu désuet) de préséance pour traduire l’anglais precedence, et distinguer cette notion de celle de
</body>
</html>
</xsl:template>
</xsl:stylesheet>
La seconde règle s’applique aux fils de T COURS U et ne fait rien. Le programme est donc très « neutre »
vis-à-vis du contenu du document source. Il se contente de produire le « cadre » de présentation HTML
ci-dessous.
Exemple 31 ExXSLTImport.html : Le document HTML produit par ExXSLTImport.xsl
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Publication XSLT</title>
</head>
<body bgcolor="white"></body>
</html>
On peut imaginer que cette règle pourrait produire un squelette HTML beaucoup plus sophistiqué, avec
menus, frames, contrôles JavaScript, et tout un attirail graphique rendant la présentation beaucoup plus
attrayante. L’intérêt est qu’une fois cette mise en page réalisée et insérée dans l’unique règle, on peut la
réutiliser en l’important dans d’autres programmes.
Voici un programme qui affiche la liste des enseignants du cours. Il commence par importer le fichier
ExXSLTImport.xsl, ce qui dispense de définir une règle s’appliquant à la racine du document. Ensuite on
redéfinit les règles s’appliquant au sous-arbre des enseignants pour produire une liste avec les noms. Ces
règles ont une préséance supérieure à celles du fichier importé qui ne produisaient aucun résultat.
Exemple 32 ExXSLTPrincipal.xsl : Un programme importateur de ExXSLTImport.xsl
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="ExXSLTImport.xsl"/>
<xsl:template match="COURS/ENSEIGNANTS">
<ol><xsl:apply-templates select="NOM"/></ol>
</xsl:template>
<xsl:template match="NOM">
<li><xsl:value-of select="."/></li>
</xsl:template>
</xsl:stylesheet>
La préséance doit être distinguée du niveau de priorité d’une règle, notion que nous exposerons page 127.
La préséance s’applique à l’importation de programme, et elle est toujours inférieure pour les règles du pro-
gramme importé par rapport aux règles du programme importateur.
Cette règle se généralise assez simplement à l’importation de plusieurs documents de la manière suiv-
ante :
S si un document VXW est importé avant un document VZY , alors toutes les règles de VCY ont une préséance
supérieure à celles de VXW ;
3.1. PROGRAMMES XSLT 117
S si une règle [\W a une préséance supérieure à [)Y , elle-même supérieure à [-] , alors [^W a une préséance
supérieure à [ ] (transitivité).
Prenons un exemple simple, illustré dans la figure 3.2. le document principal A importe d’abord B, qui
lui-même importe C et D, puis A importe E qui importe F.
B E
C D F
Les règles de C ont une préséance inférieure à celles de B, et donc inférieures à celles de A. C’est vrai
aussi des règles de D, mais elles ont une préséance supérieure à celles de C puisque D est importé après C.
Enfin les règles de F ont une préséance inférieure à celles de E, mais supérieures à toutes celles issues de
B, C ou D puisque F a été importé après. En résumé l’ordre (descendant) de préséance pour ces documents
est A, E, F, B, D et C.
Remarque : On peut voir dans cet arbre que l’ordre descendant de préséance pour ces documents corre-
spond à un parcours en post-ordre (en profondeur de droit à gauche).
L’ordre de préséance est pris en compte au moment où le processeur recherche les règles applicables
à un nœud. Si plusieurs règles sont candidates, seules celles ayant l’ordre de préséance le plus élevé sont
conservées et prises en compte. Au sein de ces règles, c’est alors l’ordre de priorité qui s’applique.
Le mécanisme d’importation s’apparente à la surcharge de méthode en programmation orienté-objet.
Dans les deux cas on réutilise les parties génériques et on redéfinit les parties spécifiques à une situation
donnée. Un inconvénient possible est que la totalité de la règle importée est remplacée par la règle du pro-
gramme local, alors que l’on peut souhaiter parfois exécuter cette règle importée tout en l’enrichissant de
quelques instructions complémentaires. C’est ce que permet l’instruction XSLT xsl:apply-imports,
présentée page 255.
1. serveur statique : les transformations sont effectuées en batch afin de produire les pages HTML à
partir d’un ou plusieurs documents XML ; les pages HTML statiques sont alors transférées sur le
serveur ;
2. serveur dynamique : cette fois la transformation s’effectue la demande en fonction des requêtes
HTTP ;
3. client dynamique : le document XML et le programme XSLT sont transmis au client qui effectue la
transformation.
La première solution est simple et ne pose pas de problème de performance puisque les pages HTML
sont directement disponibles. Elle peut souffrir d’un certain manque de réactivité, le contenu étant figé
entre deux transformations.
118 CHAPTER 3. XSLT
La plupart des processeurs permettent des transformations « statiques » à partir de la ligne de com-
mande. Voici par exemple une transformation avec Xalan, le processeur XSLT de la fondation Apache
dont l’installation est décrite dans l’annexe A. La commande transforme le document Alien.xml avec le
programme Film.xsl, et produit la page HTML Alien.html.
La seconde solution permet une adaptation aux évolutions en temps réel, mais risque de soulever des
problèmes de performances si de très nombreuses transformations doivent être effectuées sur des docu-
ments volumineux. Plusieurs environnements de publication basée sur ce mécanisme existent : Cocoon par
exemple, également produit par la fondation Apache, se base sur le processeur Xalan pour déclencher des
transformations dynamiquement en fonction de la requête HTTP, du type de navigateur utilisé, etc (voir
annexe A).
Enfin la dernière solution consiste à effectuer la transformation sur le client. À l’heure actuelle seul le
navigateur Internet Explorer 5 (IE5) dispose d’un processeur XSLT intégré. Outre les problèmes de com-
patibilité avec le navigateur, cette solution présente l’inconvénient de transmettre toutes les informations
du document XML au client, ce qui n’est pas forcément souhaitable,
L’instruction T ?xml-stylesheet? U
Le moyen le plus courant pour associer un document XML à un programme XSLT est une instruction
de traitement T ?xml-stylesheet? U qui doit apparaître dans le prologue du document à transformer.
Voici par exemple comment indiquer, dans le document Alien.xml, que le programme associé est Film.xsl.
C’est une instruction de traitement, dont le nom est xml-stylesheet, et le contenu une liste
d’attributs2 . Ces attributs sont :
S href, qui donne l’URI du programme XSLT (ce peut être un fichier local, ou un fichier accessible
sur le Web) ;
S type, qui donne le type MIME du programme XSLT : text/xml ou text/xsl sont des choix
reconnus, le premier étant celui officiellement préconisé par le W3C ;
S title, une chaîne de caractère qui peut être utilisée pour permettre le choix du programme à appli-
quer ;
S media, qui indique le format du document produit par la transformation ;
S alternate, qui vaut no si le programme XSLT référencé doit être utilisé en priorité, ou yes
sinon.
Les deux premiers attributs sont obligatoires, les autres servant essentiellement à effectuer un choix
quand plusieurs instructions T ?xml-stylesheet? U sont présentes. L’interprétation de ces attributs et
les valeurs qu’ils peuvent prendre sont en parties dépendantes du processeur ou de l’environnement. Voici
par exemple comment on peut indiquer à l’environnement de publication Cocoon une transformation par
défaut avec le programme Film.xsl, et, seulement dans le cas d’un dialogue avec un terminal WML, le
programme FilmWML.xsl.
L’utilisation de cette instruction n’est pas toujours souhaitable car elle va dans une certaine mesure à
l’encontre d’une séparation stricte du document XML et des programmes de transformation. Chaque pro-
cesseur définit ses propres modes d’application de programme XSLT. Signalons également que la recom-
mandation XSLT prévoit la possibilité d’inclure directement le programme XSLT dans le document XML
à transformer (embedded stylesheets). Dans ce cas il n’y a plus de document XSLT indépendant, ce qui
empêche de réutiliser un programme pour d’autres documents XML de même structure.
Un exemple
Le programme suivant recherche et affiche les noms des enseignants, les attributs ID et l’intitulé des
séances de cours.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="//NOM"/>
<xsl:apply-templates select="//SEANCE/@ID"/>
<xsl:apply-templates select="//SEANCE"/>
</xsl:template>
<xsl:template match="NOM">
<NOM><xsl:value-of select="."/></NOM>
</xsl:template>
<xsl:template match="@ID">
<IDSEANCE><xsl:value-of select="."/></IDSEANCE>
</xsl:template>
<xsl:template match="PROGRAMME/SEANCE">
<SEANCE><xsl:value-of select="."/></SEANCE>
120 CHAPTER 3. XSLT
</xsl:template>
</xsl:stylesheet>
On trouve dans le programme Pattern1.xsl plusieurs expressions XPath. Certaines apparaissent comme
valeur de l’attribut match de l’élément xsl:template, d’autres comme valeur de l’attribut select
de xsl:apply-templates. Elles jouent un rôle différent :
2. l’expression dans l’élément xsl:template exprime en revanche une condition sur les nœuds qui
vont permettre de déclencher la règle.
La première règle
Reprenons notre exemple 33. Le premier ensemble de nœuds considéré par le processeur XSLT est la
racine du document. On cherche donc la (ou les) règle(s) dont le pattern est satisfait par ce nœud racine. Il
n’en existe qu’une, c’est la première règle. Son pattern est « / », et en l’évaluant à partir de n’importe quel
nœud du document source, on obtient la racine la racine du document.
On déclenche alors la règle qui consiste en trois éléments xsl:apply-templates.
1. l’interprétation d’un pattern consiste à l’évaluer à partir d’un nœud contexte et à vérifier que le nœud
courant appartient au résultat : l’expression doit donc donner un ensemble de nœuds ;
2. si on acceptait toutes les expressions XPath donnant un ensemble de nœuds, il faudrait, pour vérifier
si un nœud vérifie un pattern, évaluer l’expression pour tous les nœuds du document source, à chaque
fois.
La règle suivante par exemple n’est pas valide : l’attribut match est une expression XPath correcte
mais dont le résultat n’est pas un ensemble de nœuds. La condition de déclenchement de la règle n’a donc
pas de sens.
De plus, toutes les expressions XPath donnant un ensemble de nœuds ne sont pas acceptées, essentielle-
ment pour des raisons de performance et de complexité d’implantation du processeur XSLT. La règle suiv-
ante par exemple est interdite, et ce bien que l’attribut match soit une expression XPath dont l’évaluation
donne un ensemble de nœuds.
Pour déterminer si cette règle doit être déclenchée quand on rencontre un nœud _ , il faut regarder si
on trouve un nœud en cinquième position dans l’ordre des successeurs de _ . Si un tel nœud existe, alors
l’évaluation du pattern donnera _ .
Il est clair qu’autoriser toutes les expressions XPath engendrerait des problèmes de performance, ainsi
que des difficultés de développement des processeurs XSLT. Les seuls axes autorisés dans les pattern
sont donc child et attributes ainsi que leur syntaxe abbrégé. En plus, il est possible d’utiliser la
syntaxe abbrégé // de descendant-or-self::node()/child:: (Attention, il n’est pas possible
d’utiliser l’axe descendant-or-self explicitement dans un pattern).
Cette restriction permet de décrire l’évaluation d’un pattern a par rapport à un nœud courant _ selon
l’algorithme suivant :
1. prendre _ comme nœud contexte et évaluer a ; si _ fait partie du résultat, alors _ satisfait a ;
2. sinon prendre le père de _ , et recommencer l’évaluation ; si _ fait partie du résultat, alors _ satisfait
a ;
3. sinon recommencer en parcourant les ancêtres de _ jusqu’à la racine du document.
Évidemment il est possible dans la plupart des cas d’éviter le parcours de tous les ancêtres du nœud
courant. Il faut cependant être conscient que l’évaluation d’un pattern doit être faite pour tous les nœuds
désignés par xsl:apply-templates, et qu’elle peut être longue si l’expression est complexe.
Quelques exemples
Voici quelques exemples de patterns avec leur signification.
S /COURS/ENSEIGNANTS sera satisfait par tous les nœuds de type ENSEIGNANTS fils d’un élément
racine de type COURS ;
S //SEANCE[@ID=2] ou SEANCE[@ID=2] seront satisfait par tout nœud de type SEANCE ayant
un attribut ID valant 2 (férifiez si les deux expressions sont équivalentes) ;
S NOM[position()=2] sera satisfait par tout nœud qui est le deuxième fils de type NOM de son
père ;
S *[position()=2][name()="NOM" sera satisfait par tout nœud de type NOM, second fils de
son père ;
S /COURS/@CODE[.="TC234"] sera satisfait par le nœud de type Attr, de nom CODE, fils de
l’élément racine T COURS U , et dont la valeur est TC234.
3.2.2 Règles
Une règle est définie par un élément de premier niveau xsl:template. Cet élément comprend des at-
tributs qui décrivent les conditions de déclenchement de la règle, et un contenu ou corps de règle décrivant
le texte à produire quand la règle est déclenchée (ou « instanciée » pour utiliser le vocabulaire XSLT).
L’instanciation d’une règle s’effectue toujours pour un nœud du document source qui est désigné par le
terme de nœud courant. Toute expression XPath utilisée dans le corps de la règle, que ce soit pour ef-
fectuer des tests, extraire des informations ou appeler d’autre règle, prendra le nœud courant comme nœud
contexte.
L’élément xsl:template
Les attributs sont au nombre de quatre 3 :
1. match est le pattern désignant les nœuds de l’arbre XML pour lesquels la règle peut se déclencher ;
3 La syntaxe complète de l’élément (ainsi que de tous ceux que nous présentons par la suite) est donnée dans l’annexe B, page 279.
3.2. LES RÈGLES XSLT 123
2. name définit une règle nommée qui pourra être appelée directement par son nom avec xsl:call-
template ;
3. mode permet de définir des catégories de règles, à appeler dans des circonstances particulières ;
Tous les attributs sont optionnels, mais soit name, soit match doit être défini. Dans le premier cas la
règle sera appelée par xsl:call-template, dans le second cas, c’est au processeur de déterminer, en
fonction du nœud courant et du pattern (ainsi que de la résolution des conflits éventuels), si la règle doit être
appelée. Ces deux types de règles correspondent à deux styles de programmation différents. Le premier cas
(appel de règle) s’apparente plutôt à une programmation par appel de fonction, tandis qu’avec le second,
plus « déclaratif », on se contente de « déclarer » ce qu’on veut obtenir, à charge pour le processeur de faire
le choix de la règle appropriée.
Rappelons qu’un appel à xsl:apply-templates sans attribut select est équivalent à la sélec-
tion de tous les fils du nœud courant. Cette règle est utilisée pour tous les xsl:apply-templates qui
ne trouvent pas de règle à déclencher, y compris quand un mode est indiqué.
La seconde règle par défaut s’applique aux nœuds de texte et aux attributs. Elle insère le contenu textuel
de ces nœuds dans le document résultat.
Enfin la dernière règle par défaut s’applique aux commentaires et aux instructions de traitement. Le
comportement par défaut est de les ignorer. La règle ne fait donc rien :
Voici le programme XSLT minimal : il ne contient aucune règle, à part les règles par défaut qui sont
implicites.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
</xsl:stylesheet>
Publication XSLT
Amann
Rigaux
Introduction
Documents XML
Programmation XSLT
Les règles par défaut se sont donc appliquées. Elles ont permis de parcourir tous les éléments du
document, en produisant le contenu des nœuds de type Text. Notez que les attributs ne sont pas sélectionnés
par les expressions XPath dans les xsl:apply-templates des règles par défaut, et que leur contenu
n’apparaît donc pas.
L’expression de l’attribut select doit toujours ramener un ensemble de nœuds. Sa valeur par défaut
est child::node(), autrement dit tous les fils du nœud courant, quel que soit leur type, à l’exception
comme d’habitude des attributs (voir chapitre 2).
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="//ENSEIGNANTS"/>
</xsl:template>
<xsl:template match="ENSEIGNANTS">
<xsl:comment>
Application de la règle ENSEIGNANTS
</xsl:comment>
<xsl:apply-templates/>
3.2. LES RÈGLES XSLT 125
</xsl:template>
<xsl:template match="NOM">
<xsl:value-of select="position()"/> : Noeud NOM
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="position()"/> : Noeud de texte
</xsl:template>
<xsl:template match="comment()">
<xsl:value-of select="position()"/> : Noeud de commentaire
</xsl:template>
</xsl:stylesheet>
Il contient plusieurs règles. La première déclenche un xsl:apply-templates pour tous les nœuds
de type ENSEIGNANTS. Il n’existe qu’un nœud de ce type, et une règle associée qui est instanciée.
Cette règle produit un commentaire dans le document résultat avec l’instruction xsl:comment, puis
déclenche à son tour un xsl:apply-templates sans donner de cible, ce qui revient à sélectionner
tous les fils du nœud T ENSEIGNANTS U . On obtient le résultat suivant :
Que s’est-il passé ? On peut reconnaître l’exécution de la seconde règle au commentaire. Ensuite le
processeur a pris tous les nœuds fils de T ENSEIGNANTS U et a cherché la règle à instancier. On peut
constater qu’il y a sept nœuds, dont quatre sont des nœuds de texte constitués uniquement d’espace. Nous
avons vu que ces nœuds étaient présents dans l’arbre DOM, et ils ne sont pas éliminés par le processeur
XSLT, à moins de le demander explicitement avec un xsl:strip-space : voir page 278.
En général ces nœuds sont traités « silencieusement » par le processeur : la règle par défaut pour les
nœuds de type Text s’applique, elle consiste à insérer le contenu dans le résultat, mais comme ce contenu
est constitué d’espaces, cela passe en général inaperçu.
Ici il existe une règle spécifique pour les nœuds textes, qui affiche notamment leur position. C’est
cette règle que le processeur XSLT a instanciée. On peut faire la même remarque pour le nœud de type
Comment et les éléments de type NOM pour lesquels une règle spécifique existe dans notre programme.
Priorités
La priorité d’une règle peut être soit indiquée explicitement avec l’attribut priority, soit calculée im-
plicitement par le processeur.
Prenons tout d’abord le cas où on indique explicitement une priorité. Supposons que l’on veuille
recopier tout le document ListeFilms.xml, à l’exception du résumé des films. On peut envisager de définir
une règle pour tous les types de nœuds, en recopiant leur contenu, balises comprises, sauf dans le cas des
éléments de types RESUME pour lesquels on ne fait rien.
Une solution plus simple et plus générale consiste à utiliser l’instruction xsl:copy qui recopie un
nœud du document source vers le document résultat. Dans notre cas xsl:copy doit être utilisé pour tous
les nœuds, sauf pour T RESUME U . Deux règles suffisent :
S une règle pour les éléments de type RESUME, qui ne fait rien ;
S une règle pour tous les nœuds qui effectue la copie du nœud pour lequel elle est instanciée, et qui
déclenche un xsl:apply-templates pour les fils de ce nœud.
Ces deux règles seront sélectionnées pour un nœud de type RESUME. Afin d’indiquer que la première
est prioritaire, il suffit de donner à la seconde une priorité faible, par exemple -1. Voici le programme XSLT
qui copie tous les films sauf leur résumé.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="xml" encoding="ISO-8859-1"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="RESUME"/>
</xsl:stylesheet>
</FILM>
<FILM>
<TITRE>Alien</TITRE>
<ANNEE>1979</ANNEE><GENRE>Science-fiction</GENRE>
<MES>Ridley Scott</MES>
</FILM>
<FILM>
<TITRE>Titanic</TITRE>
<ANNEE>1997</ANNEE><GENRE>Drame</GENRE>
<MES>James Cameron</MES>
</FILM>
</FILMS>
Quand une priorité explicite n’est pas indiquée, le système en calcule une en se basant sur le pattern de
l’attribut match :
1. Tous les patterns constitués d’une seule étape XPath, avec un nom d’élément ou d’attribut et sans
prédicat ont une priorité égale à 0.
Exemple : FILM, @CODE.
3. Tous les patterns constitués d’une seule étape XPath, avec un nom d’élément ou d’attribut égal à
« * » et un espace de nom ont une priorité égale à -0,25.
Exemple : xbook:*.
4. Les filtres sans espace de nom et autres qu’un nom d’élément ou d’attribut ont une priorité égale à
-0,5.
Exemple : node(), processing-instruction(), *
Enfin tous les patterns qui n’appartiennent pas à une des catégories ci-dessus ont une priorité égale à
0,5. C’est le cas par exemple des patterns avec prédicat, ou des patterns constitués de plusieurs étapes.
Il reste un cas à considérer, celui d’un pattern constitué d’une union avec l’opérateur « | ». Par exemple :
<xsl:template match="SUJET|node()">
corps de la règle
</xsl:template>
Dans ce cas le processeur constitue simplement deux règles indépendantes, et calcule la priorité pour
chacune. On se retrouve donc avec :
<xsl:template match="SUJET">
corps de la règle
</xsl:template>
<xsl:template match="node()">
corps de la règle
</xsl:template>
La première règle a une priorité égale à 0, la seconde une priorité égale à -0,5.
Si on reprend l’exemple du programme Priority.xsl, page 127, on s’aperçoit maintenant qu’il était
inutile de donner une priorité puisque celles déterminées par défaut sont les suivantes :
S RESUME a une priorité de 0 ;
3.2. LES RÈGLES XSLT 129
Donc la dernière règle est moins prioritaire, ce qui correspond à l’intuition qu’elle est moins « spécifique »,
autrement dit qu’elle s’applique à une plus grande variété de nœuds. C’est la même intuition qui mène à
affecter une priorité plus grande à un pattern comme FILM/TITRE (priorité 0,5) qu’au pattern TITRE
(priorité 0).
Modes
Les modes constituent la dernière manière de guider le processeur dans le choix d’une règle. L’attribut
mode est optionnel : en son absence la règle sera concernée par tout xsl:apply-templates qui
lui non plus n’utilise pas son attribut mode. Sinon le mode de la règle et le mode de xsl:apply-
templates doivent coïncider.
Les modes sont principalement utiles quand on souhaite placer la même information issue du document
source en plusieurs endroits du document résultat. Nous allons prendre un exemple qui suffira à illustrer
l’idée. Supposons que l’on veuille créer un document HTML avec une présentation de tous les films
présents dans ListeFilms.xml (voir page 126). La page HTML affichée dans le navigateur aura une taille
respectable. Si on veut consulter un film particulier, il faudra faire défiler la page dans la fenêtre.
Pour faciliter la tâche de l’utilisateur, on peut introduire une liste d’ancres HTML au début de la page.
La balise T A U peut être utilisée pour définir une position interne à un document en plaçant des « marques ».
Par exemple :
<a name=’Alien’/>
définit une position de nom Alien. On peut alors utiliser une ancre donnant directement accès à cette
position :
Le programme XSLT qui suit effectue deux passages sur les nœuds de type FILM, avec deux rè-
gles utilisant le même pattern mais deux modes différents. Au cours du premier passage (règle avec
mode=’Ancres) on crée un tableau HTML avec une seule ligne contenant une ancre pour chaque film.
Le second passage crée une représentation HTML complète de chaque film. Les règles sont appelées avec
deux xsl:apply-templates, avec les modes correspondant.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="FILMS">
<html>
<head><title>Liste des films</title></head>
<body bgcolor="white">
<center><table>
130 CHAPTER 3. XSLT
<xsl:apply-templates select="FILM"
mode ="Ancres"/>
</table></center>
<xsl:apply-templates select="FILM"/>
</body>
</html>
</xsl:template>
<xsl:template match="FILM">
<a name="{TITRE}"/>
<h1><xsl:value-of select="TITRE"/></h1>
<b><xsl:value-of select="TITRE"/>,</b>
<xsl:value-of select="GENRE"/>
<br/>
<b>Réalisateur</b> : <xsl:value-of select="MES"/>
<p>
<b>Résumé</b> : <xsl:value-of select="RESUME"/>
</p>
</xsl:template>
</xsl:stylesheet>
Notez la possibilité d’introduire des expressions XPath pour calculer la valeur de l’attribut d’un élément
littéral, comme dans :
<a name="{TITRE}"/>
Ce mécanisme est décrit dans le chapitre qui suit, page ?? (voir également dans la référence XSLT,
page 258). Le résultat de la transformation (ou plus exactement sa présentation avec Netscape) est donné
dans la figure 3.3. En cliquant sur les ancres placées au début du document, on se positionne directement
sur le film correspondant.
1. On restreint la liste à toutes les règles qui ont le même mode que l’élément xsl:apply-templates,
ou pas d’attribut mode si xsl:apply-templates lui-même n’en a pas.
3. Si on a trouvé plusieurs règles, on ne garde que celle(s) qui a (ont) la plus grande préséance d’importation
(voir page 115).
4. S’il reste encore plusieurs règles, on prend celle qui a la plus grande priorité.
Si cet algorithme ne permet pas de départager deux règles, le processeur XSLT peut s’arrêter ou en
choisir une (en principe la dernière dans l’ordre du programme). Il y a clairement dans ce cas un prob-
lème de conception du programme. Si, au contraire, aucune règle n’est trouvée, alors la règle par défaut
s’applique (voir page 123).
3.2. LES RÈGLES XSLT 131
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="//ENSEIGNANTS"/>
</xsl:template>
<xsl:template match="ENSEIGNANTS">
<xsl:comment>
Application de la règle ENSEIGNANTS
</xsl:comment>
<xsl:apply-templates/>
</xsl:template>
132 CHAPTER 3. XSLT
<xsl:template name="Afficher">
<xsl:value-of select="position()"/> : <xsl:value-of select="."/>
</xsl:template>
<xsl:template match="NOM">
<xsl:call-template name="Afficher"/>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="Afficher"/>
</xsl:template>
<xsl:template match="comment()">
<xsl:call-template name="Afficher"/>
</xsl:template>
</xsl:stylesheet>
L’amélioration n’est pas encore complète, et nous verrons qu’il est possible de faire mieux en utilisant
xsl:for-each ou un passage de paramètre. Le progrès notable est qu’il devient maintenant possible
de modifier l’ensemble de la sortie du programme en ne touchant qu’au corps de la règle Afficher. Le
résultat du programme est donné ci-dessous.
Il faut souligner qu’un appel avec xsl:call-template ne change pas le contexte : le nœud courant
(et donc son contenu textuel), sa position et la taille du contexte restent connus dans le corps de la règle
Afficher.
La comparaison entre xsl:call-template et un appel de fonction reste limitée. En particulier,
dans ce que nous avons vu jusqu’ici,
S une instanciation de règle ne « renvoie » pas de valeur ;
S une instanciation de règle ne prend pas d’argument autre que le nœud auquel elle s’applique ;
Nous verrons dans la section consacrée aux variables, page 138, comment stocker dans une structure
temporaire le texte produit par une règle au lieu de l’insérer directement dans le résultat.
En ce qui concerne la deuxième limitation, elle est en partie levée par la possibilité de passer des
paramètres aux règles.
3.2.6 Paramètres
L’exemple de xsl:call-template que nous avons donné précédemment offrait assez peu d’intérêt
puisqu’il n’était pas possible de varier le texte produit par Afficher en fonction du type de nœud. Nous
allons améliorer la règle en lui passant en paramètre le texte à afficher après la position du nœud courant.
Le passage de paramètres s’effectue en deux étapes. Tout d’abord on indique au niveau de la définition
de la règle, avec des éléments xsl:param, le nom du paramètre et sa valeur par défaut. La syntaxe est
3.2. LES RÈGLES XSLT 133
<xsl:template name="Afficher">
<xsl:param name="texte" select="string(inconnu)"/>
<xsl:value-of select="position()"/> :
<xsl:value-of select="$texte"/>
</xsl:template>
Remarque : Attention aux chaînes de caractères constantes dans XPath. Si on avait mis :
<xsl:param name="texte" select="inconnu"/>
le processeur XSLT aurait évalué la valeur par défaut en recherchant un élément T inconnu U fils de
l’élément courant. La fonction string() lève l’ambiguïté. Une autre possibilité est d’encadrer la chaîne par
des apostrophes simples :
<xsl:param name="texte" select=" ’inconnu’ "/>
Bien entendu on peut inverser les guillemets et les apostrophes simples.
Maintenant on peut, avant d’appeler la règle, définir les paramètres avec xsl:with-param. Voici
par exemple l’appel à Afficher en donnant au préalable au paramètre la valeur « texte vide ».
<xsl:template match="text()">
<xsl:call-template name="Afficher">
<xsl:with-param name="texte"
select="string(’texte vide’)"/>
</xsl:call-template>
</xsl:template>
Le passage des paramètres avec XSLT est d’une grande souplesse. On peut passer à une règle des
paramètres qu’elle n’attend pas, ou au contraire ne pas lui passer de paramètre. Dans le premier cas le
paramètre est tout simplement ignoré. Dans le second cas la règle prend en compte la valeur par défaut du
paramètre, si cette valeur existe.
Il est possible d’utiliser xsl:param comme un élément de premier niveau. Dans ce cas, comme il
n’y a pas eu de déclenchement de règle avec xsl:with-param pour affecter une valeur au paramètre,
c’est au processeur de déterminer quelle est la valeur du paramètre. Il existe plusieurs possibilités, dont les
deux suivantes :
1. quand le processeur est invoqué depuis la ligne de commande, une option permet en général de définir
le paramètre : c’est par exemple l’option -PARAM avec le processeur XALAN (voir l’annexe A,
page 246) ;
2. quand le processeur est intégré à un serveur web (cas de Cocoon), les paramètres sont ceux du
protocole HTTP (variables GET ou POST).
134 CHAPTER 3. XSLT
1. on ne peut pas construire une boucle sur une variable de contrôle incrémentée à chaque étape ;
2. on ne peut pas affecter une valeur à une variable dans une règle, et utiliser cette variable dans une
autre règle sans la passer comme paramètre.
La dernière propriété est importante : elle implique que les règles sont indépendantes, et que l’ordre
dans le programme aussi bien que l’ordre d’exécution ne sont pas important. Cela laisse au programmeur
la possibilité de concevoir son programme par blocs indépendants, et cela laisse surtout au processeur la
possibilité d’optimiser l’ordre d’exécution des règles, voire de les exécuter en parallèle.
XSLT n’est pas nécessairement un langage que l’on doit utiliser pour une programmation « classique »
à base de boucles, tests et appels de fonctions intensifs. Ce style de programmation est possible, mais
avec une syntaxe qui peut paraître lourde, et l’utilisation de mécanisme assez éloignés du mode habituel de
raisonnement.
Nous présentons maintenant les éléments de XSLT correspondants aux tests, boucles et variables en
nous limitant à des cas simples. La section se conclut sur un exemple plus élaboré de construction d’un
programme XSLT.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="FILMS">
<xsl:apply-templates select="FILM"/>
</xsl:template>
<xsl:template match="FILM">
<xsl:if test="ANNEE < 1970">
<xsl:copy-of select="."/>
3.3. INSTRUCTIONS DE CONTRÔLE 135
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Ce programme utilise une instruction xsl:copy-of qui copie le nœud courant ainsi que tous ses
descendants (copie en profondeur) dans le document résultat. Noter que l’opérateur « T » ne peut être
utilisé directement dans le test et doit être remplacé par une référence à l’entité prédéfinie <.
Il n’y a pas de else dans XSLT. S’il est nécessaire d’utiliser une instruction de branchement entre
plusieurs choix, on peut utiliser xsl:choose. La structure s’apparente à celle du switch dans les
langages dérivés du C (C++, Java, ...), avec deux éléments complémentaires :
S xsl:when est semblable à xsl:if, et permet d’exprimer un des choix ;
S xsl:otherwise est le choix par défaut, aucun des xsl:when n’a été instancié.
La structure du xsl:choose est donnée ci-dessous. Il doit y avoir au moins un xsl:when, mais
xsl:otherwise est optionnel.
<xsl:choose>
<xsl:when test=’expression1’>
Corps de règle 1
</xsl:when>
<xsl:when test=’expression2’>
Corps de règle 2
</xsl:when>
...
<xsl:otherwise>
Corps de règle par défaut
</xsl:otherwise>
</xsl:choose>
Le processeur évalue successivement les expressions des xsl:when, et instancie le premier pour
lequel cette évaluation donne true. Tout le reste est alors ignoré. L’ordre des xsl:when est donc
important, surtout si les choix ne sont pas exclusifs.
Le programme suivant classe (un peu arbitrairement...) les films en deux catégories : ceux qui sont
parus avant 1960 sont considérés comme anciens, les autres comme récents. Si on n’est ni dans un cas ni
dans l’autre, un message interrogatif est produit.
<xsl:template match="FILMS">
<xsl:apply-templates select="FILM"/>
</xsl:template>
<xsl:template match="FILM">
<xsl:choose>
<xsl:when test="ANNEE < 1960">
"<xsl:value-of select="TITRE"/>" est ancien
</xsl:when>
<xsl:when test="ANNEE >= 1960">
136 CHAPTER 3. XSLT
</xsl:stylesheet>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<PROGRAMME>
<xsl:apply-templates select="//PROGRAMME"/>
</PROGRAMME>
</xsl:template>
<xsl:template match="PROGRAMME">
Programme <xsl:value-of
select="concat(position(), ’/’, last())"/> :
<SEANCES>
<xsl:for-each select="SEANCE">
Séance <xsl:value-of
4 La fonction concat() construit une chaîne de caractères en concaténant ses arguments.
3.3. INSTRUCTIONS DE CONTRÔLE 137
</xsl:stylesheet>
On peut noter tout de suite qu’il aurait été possible d’obtenir exactement la même chose avec xsl:apply-
templates et une règle dédiée aux éléments de type SEANCE. La différence entre les deux approches
est essentiellement une question de style. D’un côté on peut considérer que le code est plus clair avec
xsl:for-each, et qu’il sera probablement plus efficace puisqu’il n’est pas nécessaire de recherche la
règle à instancier.
D’un autre côté on ne peut pas réutiliser une règle indépendante pour SEANCE. Un programme qui
n’utiliserait que xsl:for-each se priverait de quelques-uns des avantages de XSLT : sa relative déclar-
ativité (c’est le processeur qui cherche la règle à appliquer) et la réutilisabilité des règles dans des contextes
différents.
Voici le résultat du programme ForEach.xsl.
<SEANCES>
Séance 1/2 : Documents XML
Séance 2/2 : Programmation XSLT
</SEANCES>
L’élément xsl:for-each est inclus dans le corps d’une règle qui a été instanciée pour le nœud
T PROGRAMME U du document. Ce nœud est donc le nœud courant de la règle. De plus il fait partie
d’un contexte constitué par l’expression XPath « //PROGRAMME » du xsl:apply-templates. Ce
contexte comprend tous les nœuds de type PROGRAMME du document. Il n’en existe qu’un, ce qui explique
que le fragment suivant :
Programme <xsl:value-of
select="concat(position(), ’/’, last())"/> :
donne le résultat
Programme 1/1
On passe ensuite au xsl:for-each qui instancie, pour chaque séance, le contenu suivant :
Ce contenu illustre bien le changement de nœud courant et de contexte effectué par xsl:for-each.
Le contexte est maintenant l’ensemble des nœuds T SEANCE U fils de T PROGRAMME U (il y en a deux), et
le nœud courant est, tour à tour, chacune de ces séances. On obtient donc le résultat :
Séance 1/2 : Documents XML
Séance 2/2 : Programmation XSLT
138 CHAPTER 3. XSLT
Après xsl:for-each, le contexte redevient celui de la règle, et le nœud courant est à nouveau
T PROGRAMME U . Le fragment :
Valable pour l’année <xsl:value-of select="ANNEE"/>
insère donc logiquement l’année 2002, contenu textuel du seul fils de T PROGRAMME U de type ANNEE.
<xsl:variable name=’var3’>
Ceci est un <mot-cle>contenu</mot-cle> de variable
</xsl:variable>
La valeur de la variable var2 est un ensemble de nœuds (node-set) rassemblé sous forme d’un
arbre temporaire qui peut être utilisée comme racine dans n’importe quelle expression XPath. Par ex-
emple, l’instruction <xsl:apply-templates select="$var2/*" transforme chacun des nœuds
dans cet ensemble.
Dans le dernier cas la valeur de var3 doit être assimilée à une chaîne de caractères, et pas à un node-
set. Il n’est pas permis par exemple d’écrire une expression comme $var3/mot-cle. Cette restriction
sur le traitement des fragments résultats a été supprimée dans le processeur Xalan et la version 1.1 (WD)
de XSLT.
Une variable XSLT n’est pas modifiable et c’est donc une erreur que de définir deux xsl:variable
avec le même nom si leurs portées se chevauchent (la recommandation XSLT utilise le terme shadowing
pour ce phénomène) sauf – voici un mot qu’on entend souvent en étudiant la recommandation XSLT – si
S une des deux variables est une variable globale ;
S les deux variables sont globales mais n’ont pas la même préséance.
Les variables XSLT sont utiles, comme dans tout langage de programmation, pour stocker des valeurs
ou des résultats de calcul et éviter ainsi de répéter plusieurs fois les mêmes instructions. On peut aussi
s’en servir pour clarifier le code. Comme, en revanche, on ne peut pas affecter de nouvelles valeurs à une
variable, on ne peut pas les utiliser pour stocker des compteurs de boucles.
Une variable var peut être référencée après sa définition par $var, dans toute expression XPath. Il faut
bien souligner qu’il est possible de stocker dans une variable aussi bien des valeurs simples (un numérique
par exemple) que le résultat de l’exécution d’une ou plusieurs règles, ou d’appels à des xsl:call-
template. Tout ce qui est compris entre les balises ouvrante et fermante de xsl:variable constitue
la valeur de la variable et peut être arbitrairement complexe.
Voici deux premiers exemples simples d’utilisation de xsl:variable. Le premier reprend le pro-
gramme qui classait nos films en deux catégories. La seule modification est l’année qui est définie comme
une variable globale, ce qui permet de modifier la valeur en un seul endroit le jour venu.
Exemple 49 VarGlobale.xsl : Une variable globale
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="FILMS">
<xsl:apply-templates select="FILM"/>
</xsl:template>
<xsl:template match="FILM">
<xsl:choose>
<xsl:when test="ANNEE < $annee">
"<xsl:value-of select="TITRE"/>" est ancien
</xsl:when>
<xsl:when test="ANNEE >= $annee">
"<xsl:value-of select="TITRE"/>" est récent
</xsl:when>
<xsl:otherwise>
De quand date "<xsl:value-of select="TITRE"/>" ?
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Notez que l’on pourrait obtenir le même résultat en passant un paramètre à la règle. L’utilisation d’une
variable (globale) est plus générale qu’un paramètre puisqu’on peut l’utiliser dans plusieurs règles. Le
choix de l’un ou l’autre est donc surtout une question d’opportunité et de lisibilité du programme.
Le second exemple montre une variable locale utilisée pour stocker une phrase qui est ensuite réutilisée
plusieurs fois. Elle reprend le programme ForEach.xsl (voir page 136) affichant la liste des séances de
cours.
Exemple 50 VarLocale.xsl : Une variable locale
140 CHAPTER 3. XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<PROGRAMME>
<xsl:apply-templates select="//PROGRAMME"/>
</PROGRAMME>
</xsl:template>
<xsl:template match="PROGRAMME">
Programme <xsl:value-of
select="concat(position(), ’/’, last())"/> :
<SEANCES>
<xsl:variable name="phrase">
(valable pour l’an <xsl:value-of select="ANNEE"/>)
</xsl:variable>
<xsl:for-each select="SEANCE">
Séance <xsl:value-of
select="concat(position(), ’/’, last())"/> :
<xsl:value-of select="concat(., $phrase)"/>
</xsl:for-each>
</SEANCES>
</xsl:stylesheet>
La portée de la variable est limitée au contenu de l’élément T SEANCES U . Elle peut donc être référencée
dans le contenu du xsl:for-each qui est un frère droit, mais pas au-delà.
Il s’agit ici d’un exemple très simple d’une utilisation courante des variables : associer à un le résultat
d’un calcul. Les variables peuvent aussi être utilisées pour résoudre des problèmes plus complexes comme
des itérations par récursion sur des ensembles de nœuds (voir page 141).
<xsl:template match="FILMS">
<FILMSTRIES>
<xsl:apply-templates select="FILM">
<xsl:sort select="ANNEE"/>
<xsl:sort select="TITRE"/>
</xsl:apply-templates>
</FILMSTRIES>
</xsl:template>
<xsl:template match="FILM">
<b>Année : </b> <xsl:value-of select="ANNEE"/>,
<xsl:value-of select="TITRE"/> réalisé
par <xsl:value-of select="MES"/>
</xsl:template>
</xsl:stylesheet>
L’expression XPath utilisée dans l’attribut select est évaluée en prenant successivement chaque
nœud de l’ensemble sélectionné par xsl:apply-templates comme nœud courant. Le tri est ensuite
effectué sur l’ensemble des valeurs obtenues. Dans l’exemple ci-dessus, chaque nœud est de type FILM,
et les critères de tri sont les fils T ANNEE U et T TITRE U de chaque film. On pourrait également utiliser
position().
Par défaut le processeur considère que le critère de tri est de type string, et les comparaisons se font
donc d’après l’ordre alphanumérique sur les chaînes de caractères (attention, dans ce cas « 10 » est plus
petit que « 2 » puisque la comparaison s’effectue de gauche à droite). On peut indiquer un attribut data-
type avec comme valeur number pour changer le mode de comparaison. Les quelques autres options de
xsl:sort sont présentées dans l’annexe B, page 277.
Nous présentons ci-dessous deux techniques de programmation pour résoudre ces deux problèmes.
Elles s’appuient toutes deux sur un appel récursif à une règle nommée, et utilisent des variables XSLT.
Le premier exemple consiste à afficher un nombre fixé d’éléments contenus dans un node-set. On
va utiliser un paramètre représentant le numéro de l’élément courant à afficher, et passer ce paramètre à une
règle nommée AfficheElement en même temps que l’ensemble de nœuds. Cette règle procède alors
de la manière suivante :
<xsl:call-template name="AfficheElement">
<xsl:with-param name="numero" select="$numero + 1"/>
<xsl:with-param name="liste" select="$liste"/>
<xsl:with-param name="max" select="$max"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
Il suffit de définir deux variables, $maxElements, et $liste, puis d’appeler cette règle en lui
passant les deux paramètres et un numéro initialisé à 1. Voici par exemple l’utilisation de cette règle pour
afficher deux films (le programme complet s’appelle BoucleFilms.xsl).
<xsl:template match="FILMS">
<xsl:call-template name="AfficheElement">
<xsl:with-param name="numero" select="1"/>
<xsl:with-param name="liste" select="$listeFilms"/>
<xsl:with-param name="max" select="$maxFilms"/>
</xsl:call-template>
</xsl:template>
3.3. INSTRUCTIONS DE CONTRÔLE 143
Notez que la variable $listeFilms est un node-set avec tous les titres. Cette variable et $max-
Films sont locales à la règle, et doivent donc être passées en paramètre puisqu’elles ne sont pas visibles
dans la règle nommée AfficheElement.
Voici un deuxième exemple d’appel récursif qui consiste cette fois à effectuer un calcul en parcourant
une liste de nœuds. Pour simplifier la présentation, nous allons prendre l’exemple très simple d’une totali-
sation, bien qu’il existe une fonction prédéfinie (sum()) pour arriver au même résultat bien plus facilement.
Cet exemple doit donc surtout être considéré comme représentatif d’une technique générale.
Le document sur lequel nous allons travailler est une liste de films avec, pour chaque film, des notes
critiques.
L’objectif est de calculer, pour chaque film, la moyenne des notes et de l’afficher dans un tableau
HTML. Pour cela on doit sélectionner, avec une expression XPath, la liste des notes pour chaque film. La
fonction count() va nous donner le nombre de notes, et on va parcourir la liste pour calculer le total des
notes. La division des deux nous donnera la moyenne.
Le principe à appliquer est celui d’un parcours récursif de la liste en additionnant, à chaque étape, le
premier élément de la liste avec la somme des éléments du reste de la liste. Le pseudo-code ci-dessous
144 CHAPTER 3. XSLT
montre le fonctionnement d’une telle fonction. On peut remarquer qu’il n’y a pas besoin de variable (et à
plus forte raison d’affectation).
Voyons comment réaliser cette fonction avec XSLT. On va, comme précédemment, utiliser un appel
récursif à une règle nommée. La différence principale est que cette fois l’appel doit « retourner » une
valeur, alors que dans notre premier exemple (programme BoucleFilms.xsl) la règle effectuait une tâche
habituelle d’insertion dans le document résultat.
La notion de « retour » de valeur n’existe pas en XSLT, mais on va utiliser à la place la possibilité de
stocker le fragment produit par une règle non pas dans le document résultat, mais dans une variable.
Voici la règle nommée. Elle suit de très près la structure de la fonction Somme() ci-dessus, avec beau-
coup plus de syntaxe, XSLT étant un langage assez bavard.
<xsl:template name="CalculTotal">
<xsl:param name="listeNotes"/>
<xsl:choose>
<xsl:when test="$listeNotes">
<xsl:variable name="note" select="$listeNotes[1]"/>
<xsl:variable name="autresNotes">
<xsl:call-template name="CalculTotal">
<xsl:with-param name="listeNotes"
select="$listeNotes[position() != 1]"/>
</xsl:call-template>
</xsl:variable>
Dans le xsl:when, qui correspond au cas où la liste n’est pas vide, on crée une variable $note avec
la première note de la liste. Puis on crée une seconde variable qui va contenir le résultat de l’appel récursif
à la règle nommée CalculTotal, en lui passant le reste de la liste.
Cette règle contient un seul xsl:value-of qui insère dans le résultat le total des deux variables.
Cette insertion correspond au « retour » de cette règle, vue comme une fonction. Voici le programme
complet, le résultat étant donné dans la figure 3.4.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="FILMS">
3.3. INSTRUCTIONS DE CONTRÔLE 145
<html>
<head><title>Statistiques</title></head>
<body bgcolor="white">
<center>
<h1>Moyenne des notes pour chaque film</h1>
<table border=’1’>
<xsl:apply-templates select="FILM"/>
</table>
</center>
</body>
</html>
</xsl:template>
<xsl:template match="FILM">
<xsl:variable name="total">
<xsl:call-template name="CalculTotal">
<xsl:with-param name="listeNotes"
select="NOTES/NOTE"/>
</xsl:call-template>
</xsl:variable>
<tr>
<td><b><xsl:value-of select="TITRE"/></b></td>
<td align="right">
<xsl:value-of select="$total div count(NOTES/NOTE)"/>
</td>
</tr>
</xsl:template>
<xsl:template name="CalculTotal">
<xsl:param name="listeNotes"/>
<xsl:choose>
<xsl:when test="$listeNotes">
<xsl:variable name="note" select="$listeNotes[1]"/>
<xsl:variable name="autresNotes">
<xsl:call-template name="CalculTotal">
<xsl:with-param name="listeNotes"
select="$listeNotes[position() != 1]"/>
</xsl:call-template>
</xsl:variable>
</xsl:stylesheet>
Cet exemple illustre la puissance de XSLT en tant que langage de programmation. Il reste que la
faiblesse du typage, combinée à une approche « fonctionnelle » de la programmation peuvent rapidement
rendre très laborieux le développement de programme XSLT effectuant des opérations complexes. Il faut
par exemple envisager de stocker des listes dans des chaînes de caractères avant de les traiter par des appels
récursifs. Ce type de programmation trouve rapidement ses limites en terme de maintenance et d’évolution.
Il est sans doute préférable de considérer avant tout XSLT (au moins pour la version 1.0) comme un langage
146 CHAPTER 3. XSLT
orienté vers la transformation de documents XML, et capable occasionnellement d’effectuer des tâches de
programmation classiques.
Document
-
Element
COURS
Nous prenons l’exemple d’une transformation du document CoursXML.xml, page 110. La figure 3.5
illustre la représentation DOM de ce document. La transformation construit une page HTML. Voici le
3.4. ÉVALUATION D’UN PROGRAMME XSLT 147
programme.
Exemple 55 Recap.xsl : Récapitulatif de l’évaluation d’un programme XSLT
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head><title>
<xsl:value-of select="COURS/SUJET"/>
</title></head>
<body bgcolor="white">
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="SUJET">
<h1><center><xsl:value-of select="."/></center></h1>
</xsl:template>
<xsl:template match="ENSEIGNANTS">
<h1>Enseignants</h1>
<ol>
<xsl:apply-templates select="NOM"/>
</ol>
</xsl:template>
<xsl:template match="PROGRAMME">
<h1>Programme</h1>
<ul>
<xsl:for-each select="SEANCE">
<xsl:call-template name="AfficheSeance"/>
</xsl:for-each>
</ul>
</xsl:template>
<xsl:template match="ENSEIGNANTS/NOM">
<li><xsl:value-of select="." /></li>
</xsl:template>
<xsl:template name="AfficheSeance">
<li> Séance <xsl:value-of select="concat (
position(), ’/’, last(), ’ : ’, .)"/>
</li>
</xsl:template>
</xsl:stylesheet>
Après l’analyse des deux documents, le processeur va commencer par créer le nœud racine du document
résultat (figure 3.6), et par se positionner sur la racine du document source qui devient le nœud courant.
De plus le processeur associe à chaque nœud courant une liste de nœud (le « contexte ») contenant le nœud
courant lui-même, avec une position donnée, et éventuellement d’autres nœuds.
148 CHAPTER 3. XSLT
Document
-
Prenant maintenant la liste des règles du programme, le processeur recherche celles qui sont applicables
au nœud racine du document, en appliquant l’algorithme présenté page 1265 .
La seule règle qui convient est la première. Elle est donc instanciée : son contenu est copié sous la
racine du document résultat, ce qui donne l’arbre provisoire de la figure 3.7.
Document
-
html
head body
title xsl:apply-templates
Attr
xsl:value-of select
COURS/SUJET
Cet arbre contient deux éléments dont l’espace de nom est xsl. Ils doivent donc être évalués par le
processeur et remplacés par le résultat obtenu. On voit bien dès ce stade que l’évaluation d’un programme
XSLT n’est pas un processus linéaire dans lequel les balises ouvrante et fermante des éléments sont pro-
duites séquentiellement. Un arbre est construit par instanciation de nœuds complets, et à ces nœuds sont
rattachés de nouveaux fils au fur et à mesure que les instructions XSLT restantes sont évaluées. Ici encore,
il vaut donc mieux raisonner, comme nous le faisons depuis le début de ce livre, sur une représentation
arborescente des documents que sur une version sérialisée avec balises.
Une particularité de ce type d’évaluation est que le processeur a le choix de la prochaine instruction
à évaluer. On voit bien sur la figure 3.7 que les deux instructions xsl:value-of et xsl:apply-
templates peuvent être évaluées séparément, dans un ordre quelconque, voire en parallèle. Il suffit que
le processeur conserve l’information sur le nœud courant et le contexte associé à chacune de ces instructions
pour pouvoir y revenir et les traiter à tout moment.
Si on choisit d’évaluer d’abord xsl:value-of, on obtient l’arbre temporaire de la figure 3.8. À partir
du nœud courant qui est la racine du document source, on a évalué l’expression XPath « /COURS/SUJET »,
et on a remplacé l’instruction XSLT par le résultat de cette évaluation, la chaîne « Programmation XSLT ».
soit le même.
3.4. ÉVALUATION D’UN PROGRAMME XSLT 149
Document
-
html
head body
title xsl:apply-templates
XSLT
La recherche dans le programme d’une règle applicable à ce nœud échoue, donc le processeur choisit la
règle par défaut (voir page 123). Elle consiste à sélectionner les fils du nœud courant. On a donc maintenant
un ensemble de trois éléments, T SUJET U , T ENSEIGNANTS U et T PROGRAMME U .
Le processeur va prendre ces trois éléments tour à tour comme nœud courant, rechercher la règle pour
chacun, et l’instancier. Il existe trois règles distinctes dans le programme, ce qui donne l’arbre résultat
temporaire de la figure 3.9.
Document
-
html
head body
title h1 h1 h1
XSLT center ol ul
Attr
name
AfficheSeance
Le processeur continue à prendre dans l’arbre résultat en cours de constitution les éléments dont
l’espace de nom est xsl. Pour chacun de ces éléments il existe un nœud courant dans l’arbre source,
lui-même faisant partie d’un ensemble de nœuds. Pour l’élément xsl:value-of dans la figure 3.9 par
150 CHAPTER 3. XSLT
exemple, le nœud courant dans l’arbre source est T SUJET U , et l’ensemble de nœuds est constitué des
trois fils de T COURS U . À partir de la situation de la figure 3.9, le processeur va donc (en supposant que
les instructions sont traitées dans l’ordre du document) :
1. évaluer xsl:value-of, avec l’attribut select valant « . », le nœud courant étant T SUJET U : le
résultat est XSLT ;
2. évaluer l’expression NOM de l’attribut xsl:select du xsl:apply-templates, le nœud courant
étant T ENSEIGNANTS U : on obtient deux nœuds pour lesquels la seule règle qualifiée est celle dont
le pattern est « ENSEIGNANTS/NOM » ;
3. enfin l’expression SEANCE de l’attribut xsl:select du xsl:for-each est évaluée, ce qui
donne (le nœud courant étant T PROGRAMME U ) les deux éléments de type SEANCE du document
source.
La figure 3.10 montre la situation de l’arbre résultat intermédiaire après évaluation du xsl:for-
each. Il reste au processeur à évaluer deux appels xsl:call-template à la règle nommée Affich-
eSeance. En apparence ces deux appels sont identiques, mais ce qui les distingue (non représenté sur la
figure), c’est leur nœud courant.
Document
-
html
head body
title h1 h1 h1
XSLT center ol ul
Attr Attr
Amann Rigaux name name
AfficheSeance AfficheSeance
L’instruction xsl:for-each a pour effet de changer le nœud courant pour chaque instanciation de
son contenu (voir page 136). L’ensemble de nœuds, lui, est constitué par l’évaluation de l’expression du
xsl:for-each, et se trouve donc commun aux deux appels de règles.
Un appel à xsl:call-template ne change ni le nœud courant (une des deux séances), ni l’ensemble
des nœuds formant le contexte (les deux séances). L’instanciation de la règle nommée donnera donc le ré-
sultat final de la figure 3.11.
Document
-
html
head body
title h1 h1 h1
XSLT center ol ul
XSLT li li li li
<h1>
<center>Publication XSLT</center>
</h1>
<h1>Enseignants</h1>
<ol>
<li>Amann</li>
<li>Rigaux</li>
</ol>
<h1>Programme</h1>
<ul>
<li> Séance 1/2 : Documents XML</li>
<li> Séance 2/2 : Programmation XSLT</li>
</ul>
</body>
</html>
Cet exemple a montré, sur un cas simple mais représentatif, les principales caractéristiques de l’évaluation
d’un programme XSLT. Récapitulons les points les plus importants :
nœuds : ces deux informations constituent le contexte d’évaluation des expressions XPath placées
dans l’instruction ;
3. on ne peut pas préjuger de l’ordre d’exécution des instructions d’un programme XSLT : le langage
lui-même est conçu pour que le résultat ne dépende pas de cet ordre.
Chapter 4
Sommaire
4.1 Définition de Types de Documents : DTD . . . . . . . . . . . . . . . . . . . . . . . . 154
4.1.1 Pourquoi définir une DTD ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
4.1.2 Entités . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
4.1.3 Éléments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
4.1.4 Attributs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
4.1.5 DTD et XML Schéma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
4.2 Site Web: HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
4.2.1 HTML et XHTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
4.2.2 DTD XHTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
4.2.3 Génération de pages HTML: xsl:output . . . . . . . . . . . . . . . . . . . 170
4.2.4 Transformation d’une page XML . . . . . . . . . . . . . . . . . . . . . . . . . 171
4.2.5 Création de liens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
4.2.6 XML/XSLT: une solution du problème des liens cassés . . . . . . . . . . . . . 175
4.2.7 Intégration de pages XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
4.3 Présentations multimédia: SMIL . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
4.3.1 SMIL par un exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
4.3.2 DTD SMIL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
4.3.3 Génération de présentations SMIL . . . . . . . . . . . . . . . . . . . . . . . . 187
4.4 Traitement de texte dans XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
4.4.1 xsl:preserve-space et xsl:strip-space . . . . . . . . . . . . . . . 195
4.4.2 Génération de texte : xsl:text . . . . . . . . . . . . . . . . . . . . . . . . . 197
4.4.3 Sérialisation du résultat : xsl:output . . . . . . . . . . . . . . . . . . . . . 197
4.5 RSS et RDF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Nous allons maintenant étudier l’utilisation de XSLT pour la traduction de documents XML dans dif-
férents formats standards de publication. Nous prenons comme motivation principale l’évolution d’un site
web à partir d’un ou plusieurs documents XML et de transformations XSLT, mais les techniques étudiées
s’appliquent bien entendu à un contexte plus général.
Notre site a pour ambition de diffuser le plus largement possible son contenu sur le Web. Il ne se con-
tente donc pas de pages HTML, mais propose également de nombreux autres formats adaptés à différents
médias, et souhaite se fraire connaître le plus précisément possible auprès des moteurs de recherches et
annuaires web.
Nous avons déjà donné dans l’introduction un aperçu du langage WML qui est un dialecte XML (nous
allons également préciser cette notion de dialecte) pour la création de documents (cartes) très simples
adaptés à l’affichage sur les petits écrans des téléphones mobiles. Nous ne revenons pas ici sur WML, la
présentation du chapitre 1 suffisant à comprendre ce langage très simple. En revanche nous présentons la
153
154 CHAPTER 4. PRODUCTION DE DOCUMENTS XML
création de documents plus sophistiqués avec des images et des animations avec un autre dialecte XML
connu sous le nom de SMIL (Synchronised Multimedia Integration Language). SMIL permet de créer des
présentations animées qui intègrent du texte, des images du son et de la vidéo. Un aspect important de ce
langage est qu’il permet de synchroniser l’affichage des différents objets multimédia.
Pour augmenter sa visibilité sur le Web, le site peut produire un « sommaire » qui contient pour chaque
ressource web son titre, son URL et une description courte de son contenu. Ce sommaire est un document
RSS (RDF Site Summary) qui peut facilement être échangé (généralement ce document et beaucoup plus
compact que la ressource elle-même) avec d’autres applications sur le Web.
La caractéristique commune de tous ces formats (HTML, SMIL, WML, RSS) est qu’ils sont tous définis
sous forme d’une DTD (Document Type Definition) qui, d’une manière similaire à la langue Française
ou Allemande, définit un vocabulaire de noms d’éléments et d’attributs qui peuvent être combinés en
accord avec une grammaire. XML, de ce point de vue, est un « langage pour créer des langages » ou
« metalangage ». Nous commençons par expliquer cette notion de grammaire avant de prendre quelques
exemples pratiques pour illustrer son importance dans l’écriture de programmes de transformation.
Les processeurs XSLT permettent généralement de choisir entre une analyse syntaxique (par rapport à
la syntaxe XML) ou une analyse validante (par rapport à une DTD) des documents à transformer.
4.1.2 Entités
Dans les chapitres précédents nous avons utilisé plusieurs fois des entités pour fragmenter physiquement
le contenu sérialisé d’un document XML en plusieurs fichiers ou inclure des caractères spéciaux comme
<, >, &, ’ et ". D’une manière plus générale, la notion d’entité permet de déconnecter la définition logique
d’un document XML sous forme d’arbre DOM de sa représentation physique sous forme de fichiers XML :
un document XML peut ainsi être représenté par un ensemble d’entités très divers comme :
S plusieurs fichiers XML, dites entités XML (parsed entities) ;
S un fichier DTD optionnel ;
S plusieurs fichiers image, son et vidéo, appelés entités non-XML (unparsed entities).
4.1. DÉFINITION DE TYPES DE DOCUMENTS : DTD 155
Parmi les entités XML, un fichier, appelé entité document, correspond à la racine du document XML
et contient, directement ou indirectement, les références vers tous les autres entités. Chacune des entités
XML peut contenir à son tour des références vers d’autres entités faisant partie du document (les entités
non-XML ne peuvent pas contenir de telles références).
On obtient ainsi un graphe de référencement avec toutes les entités participant à la constitution du doc-
ument XML (arbre DOM) final par un parseur. Cette constitution consiste essentiellement à remplacer les
références vers les entités par leur contenu. Pour éviter des arbres DOM de taille infinie, ce graphe de
référencement doit être acyclique : aucune entité ne doit se référencer elle-même directement ou indirecte-
ment.
Pour pouvoir « utiliser » ou référencer une entité elle doit être définie au préalable dans la DTD du
document. La seule exception à cette règle est la définition de la DTD elle-même, qui peut être stockée
dans une entité DTD séparée et qui est définie et utilisée dans l’entité document avec la clause <!DOCTYPE
...>.
La définition d’une entité consiste à associer un contenu (XML ou autre) à un nom d’entité. XML
distingue entre les entités internes dont le contenu est défini dans la DTD et les entités externes dont le
contenu est un fichier séparé.
Une définition d’entité interne associe directement un nom à une valeur sans passer par une indirection
à travers un fichier externe. Par exemple, l’entité suivante définit une entité Ucirc avec comme valeur
l’Unicode du symbole ’Û’.
<!ENTITY Ucirc "Û">
Dans la définition d’un entité externe, le nom de l’entité est associé à l’adresse d’un fichier (URL) qui
définit son contenu. Voici les formes de définition d’entités externes :
<!ENTITY "nom_entité" SYSTEM "url_entity">
<!ENTITY "nom_entité" PUBLIC "id_publique" "url_entity">
La deuxième définition utilise un identificateur public en plus de l’URL de l’entité et permet une iden-
tification plus formelle et surtout indépendante d’une adresse physique. Il est surtout appliqué à des docu-
ments « publics » qui sont catalogués et largement référencés par d’autres documents.
Par exemple, la DTD XHTML définit une entité externe qui contient les définitions d’entités spécifiques
à l’utilisation de caractères latin dans un document XML (le symbole % après la balise <!ENTITY indique
qu’il s’agit d’une entité paramètre qui pourra être référencé dans la DTD même : voir plus bas).
<!ENTITY % HTMLlat1 PUBLIC
"-//W3C//ENTITIES Latin 1 for XHTML//EN"
"xhtml-lat1.ent">
Dans le cas où l’URL xhtml-lat1.ent est inaccessible, un parseur « intelligent » peut se servir de
l’identificateur public pour trouver dans une liste d’URL (catalogue) une URL accessible (rien n’empêche
d’ailleurs le parseur d’avoir une copie locale de cette DTD pour améliorer les performances).
Entités paramètres Comme pour un document XML, il est possible de découper une DTD en plusieurs
entités (fichiers). Ces entités sont appelées des entités paramètres pour les distinguer des entités générales
qui sont utilisées dans le contenu d’un document XML. La définition d’une entité paramètre est caractérisée
par l’ajout du symbole %. Une référence vers une entité paramètre se compose du symbole % suivi par le
nom de l’entité et terminé par le symbole ;. Voici la définition et l’utilisation d’une entité paramètre externe
dans la DTD XHTML :
<!ENTITY % HTMLlat1 PUBLIC
"-//W3C//ENTITIES Latin 1 for XHTML//EN"
"xhtml-lat1.ent">
%HTMLlat1;
Les trois premières lignes définissent l’entité qui est référencée dans la quatrième ligne par son nom. Le
résultat est l’inclusion de la DTD "xhtml-lat1.ent" - qui définit des entités pour les caractères latin - dans la
DTD XHTML.
156 CHAPTER 4. PRODUCTION DE DOCUMENTS XML
Entités non-XML et notations : Les entités non-XML comme les images et les fichiers son ont un statut
très particulier dans le monde XML. En réalité, ce type d’entité est une boîte noire qui ne peut pas être
analysée ou utilisée d’un manière « directe ». Pour pouvoir donner un minimum d’interprétation, XML
permet la spécification de formats d’entités non-XML sous forme de déclarations de notations (nous avons
mentionné dans la section consacrée à DOM page 64 l’existence d’une interface Notation) : une notation
associe un nom de format à un identificateur système ou public qui pourra être exploité pour l’interprétation
du contenu de l’entité. Généralement, cet identificateur définit une application qui peut se charger de
l’affichage ou du traitement des entités non-XML de ce format.
Par exemple, la définition de format suivante associe le format gif à une application xv :
<!NOTATION gif SYSTEM ’/usr/local/bin/xv’ >
Attention, le nom gif est choisi librement et indépendant du format de l’entité.
Pour définir une entité non-XML du format gif, on peut ensuite utiliser une syntaxe identique à celle
de la définition d’entité externe, étendue par le mot réservé NDATA, suivi du nom du format :
<!ENTITY logo SYSTEM "image.gif" NDATA gif >
Une entité non-XML ne peut pas être référencée dans le contenu d’un document XML, qui est obli-
gatoirement conforme à la syntaxe XML – ce qui ne serait très probablement plus le cas après l’inclusion
d’une image GIF dans le contenu même. La seul possibilité de référencer des entités est de définir des
attributs de type ENTITY ou ENTITIES qui peuvent ensuite prendre comme valeur un nom ou une liste
de noms d’entités non-XML.
4.1.3 Éléments
Une DTD définit le vocabulaire et la grammaire d’un « dialecte » XML. Le vocabulaire est essentiellement
constitué de noms d’éléments et d’attributs. La grammaire définit, pour chaque élément, ses fils et ses
attributs possibles.
La définition générale d’un élément dans une DTD est la suivante :
<!ELEMENT nom structure>
Contrainte : Il ne peut avoir qu’une seule définition pour chaque nom d’élément dans une DTD.
Prenons comme exemple la DTD XHTML qui décrit la dernière version de HTML (la version 4.0)
comme un dialecte XML (voir page 165 pour une description complète). La DTD complète peut être
téléchargée à l’URL http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd.
Voici la définition de l’élément racine d’un document HTML dans la DTD XHTML :
<!ELEMENT html (head, body)>
Le mot réservé <!ELEMENT est suivi du nom de l’élément à définir (html). La structure de l’élément
html est définie par (head, body).
Une DTD distingue cinq types de structures pour les éléments :
1. Si un élément est vide, on utilise le mot réservé EMPTY pour définir sa structure.
2. Si un élément ne peut contenir que du texte (des fils de type Text au sens DOM) on utilise le mot
réservé #PCDATA. Par exemple, l’élément T title U de XHTML ne peut contenir que du texte :
3. Si élément ne peut contenir que des fils qui sont à leur tour des éléments, on spécifie les types des
éléments fils possibles pour cet élément, le nombre d’occurences pour chaque fils et, éventuellement,
l’ordre dans lequels ces fils doivent apparaître dans le document (rappelons qu’un document XML
correspond à un arbre ordonné). Cette spécification est appelée le modèle de contenu de l’élément.
4.1. DÉFINITION DE TYPES DE DOCUMENTS : DTD 157
4. Si un type d’élément peut avoir un contenu « mélangé », c’est-à-dire contenir des fils qui sont des
éléments mais également du texte, on spécifie les types des éléments qui peuvent être fils de cet
élément. Dans ce cas, on ne peut plus spécifier le nombre d’occurences pour chaque fils ou l’ordre
dans lequel ces fils doivent apparaître dans le document.
5. Il est également possible de ne définir aucune contrainte sur le contenu d’un élément en utilisant le
mot réservé ANY. Dans ce cas, le contenu de l’élément doit seulement être bien-formé, c’est-à-dire
essentiellement respecter l’imbrication des balises.
Modèles de Contenu
Le modèle de contenu d’un élément indique les types de ses fils, mais également le nombre d’occurrences
pour chaque type de fils. Si nécessaire, on peut également établir un ordre sur les occurrences des fils de
chaque type.
La définition de la balise T html U indique que l’élément racine de type html ne peut avoir que deux
types d’éléments comme fils : head et body. En plus, chaque type de fils ne doit apparaître qu’une seule
fois et dans l’ordre indiqué. En d’autres termes, l’élément racine d’un document XHTML est toujours de
la forme suivante :
<html>
<head>...</head>
<body>....</body>
</html>
Pour indiquer qu’un type d’élément peut apparaître zéro ou une fois on utilise le symbole ?. Par
exemple, la DTD suivante accepte les deux documents <A><C/></A> et <A><B/><C/></A> (B et C
sont définis comme éléments vides) :
<!ELEMENT A (B?,C) >
<!ELEMENT B EMPTY >
<!ELEMENT C EMPTY >
Il existe également des symboles pour indiquer qu’un type d’élément peut apparaître plusieurs fois. On
utilise le symbole * si un type de fils peut apparaître zéro, une ou plusieurs fois, et le symbole + s’il doit
apparaître au moins une fois. Par exemple, si on remplace la définition précédente de l’élément A par la
définition :
<!ELEMENT A (B*,C+)>
la DTD accepte, entre autre, tous les éléments de type A suivants :
<A><C/></A>
<A><C/><C/></A>
<A><B/><C/></A>
<A><B/><B/><C/></A>
En revanche, cette dernière définition n’accepte pas les éléments <A></A> et <A><C/><B/><C/></A>.
Dans le premier cas, par définition, un élément vide T C/ U doit apparaître au moins une fois, et dans le
deuxième cas, tous les éléments T B/ U doivent apparaître avant tous les éléments T C/ U .
On peut donner le choix entre deux types d’éléments en remplaàant la virgule entre les éléments par le
symbole | :
<!ELEMENT A (B|C)>
Dans ce cas, A peut contenir un seul fils qui est soit de type B ou de type C.
Finalement, on peut enlever la restriction sur l’ordre des fils : on voudrait par exemple accepter comme
fils pour l’élément T A U zéro ou plusieurs éléments de type T B U et T C U dans n’importe quel ordre. Pour
cela, on peut utiliser des parenthèses pour regrouper des éléments. La définition suivante indique que les
éléments de type A acceptent un élément de type B ou de type C zéro ou plusieurs fois :
158 CHAPTER 4. PRODUCTION DE DOCUMENTS XML
<!ELEMENT A (B|C)*>
Tous les éléments de type A qui précèdent sont valides par rapport à cette définition, ainsi que les deux
éléments suivants :
<A><C/><C/><B/><C/><B/></A>
<A><B/><B/></A>
Ce parenthésage permet de définir des structures plus complexes. Par exemple, la structure d’un tableau
HTML est définie par l’expression suivante (nous ne donnons pas la structure des éléments qu’il contient) :
<!ELEMENT table
(caption?, (col*|colgroup*), thead?, tfoot?, (tbody+|tr+))>
S les éléments caption, thead et tfoot sont optionnels ;
S l’élément caption est suivi par zéro ou plusieurs éléments de type col ou colgroup (il ne peut
pas avoir simultanément les deux types d’éléments) ;
S les éléments thead et tfoot, sont suivis par un ou plusieurs éléments de type tbody ou tr (il ne
peut pas avoir les deux types d’éléments)
Contenu mixte
Les éléments de type td et th peuvent avoir un contenu mixte, c’est-à-dire contenir des sous-éléments
mais également du texte. Voici un tableau complet exprimé en XHTML (les éléments T td U contiennent
du texte) :
<table border=’1’>
<tr><td>ligne <b>1</b>, colonne <b>1</b></td>
<td>ligne <b>1</b>, colonne <b>2</b></td>
</tr>
<tr><td>ligne <em>2</em>, colonne <em>1</em></td>
<td>ligne <em>2</em>, colonne <em>2</em></td>
</tr>
</table>
</body>
</html>
Le tableau a deux lignes, indiquées par les balises T tr U dont chacune a deux colonnes (balises
T td U ). La figure 4.1 montre l’affichage par de cette page par Netscape.
Les éléments de type td et th peuvent contenir des sous-éléments de plusieurs dizaines de types
différents et une description détaillée dépasse le cadre de ce livre. Voici une définition très simplifiée du
type td :
Cette dernière définition indique que les éléments de type td contiennent des sous-éléments de dif-
férents types mélangés avec du texte dans n’importe quel ordre. En fait, il n’est pas possible de spécifier
des contraintes plus fortes sur la structure d’un élément qui contient du texte mélangé avec des éléments.
Par exemple, il n’est pas possible de spécifier qu’un élément de type td contient d’abord du texte suivi
d’autres éléments comme dans l’exemple suivant :
<!-- Attention: La déclaration suivante n’est pas possible: -->
<!ELEMENT td "(#PCDATA, (table | p | b | em )*)">
Définition récursives
La définition d’un type d’élément e donné peut contenir des types d’éléments f qui utilisent déjà e dans
leur propre définition. Si on regarde la définition (partielle) de la structure des éléments de type td, on
voit qu’une colonne d’un tableau peut à nouveau contenir un tableau, autrement dit qu’on peut créer des
tableaux imbriqués. Voici un exemple (voir l’affichage 4.2)
,:
</head>
<body>
<table border="1">
<tr> <!-- première ligne du tableau "extérieur" -->
<td> <!-- première colonne "extérieure" -->
<table border="1"> <!-- tableau "intérieur" -->
<tr><td>1</td><td>2</td></tr>
<tr><td>3</td><td>4</td></tr>
</table>
</td>
<td>a</td> <!-- deuxième colonne "extérieure" -->
</tr>
<tr> <!-- deuxième ligne du tableau "extérieur" -->
<td>b</td><td>c</td>
</tr>
</table>
</body>
</html>
4.1.4 Attributs
Une DTD sert également à spécifier les attributs permis pour chacun de ses types d’élément. Par exemple,
dans le fichier TableauComplexe.html les éléments de type table contiennent chacun un attribut border,
dont la valeur est égale à 1 (les champs du tableau doivent être entourés d’un cadre dont la largeur est un
pixel). Cet attribut est défini dans la DTD XHTML de la manière suivante :
Le mot réservé <!ATTLIST est suivi du nom de l’élément défini dans la DTD (table), du nom de
l’attribut à définir (border), des informations sur le type de l’attribut (CDATA) et son utilisation (#IMPLIED).
Le type CDATA indique que les valeurs de cet attribut peuvent être des chaînes de caractères composées de
tous les caractères sauf &, < et >. Le mot #IMPLIED signifie que cet attribut est optionnel.
Contrainte : Il ne peut avoir deux définitions d’attributs avec le même nom pour un élément. En revanche,
il est possible de définir plusieurs attributs avec le même nom (et du même type ou de types différents) dans
des éléments différents.
Dans la DTD XHTML, les éléments de type td et th (champs dans une ligne d’un tableau) partagent
exactement les mêmes attributs. Voici une version simplifiée :
La présence de certains attributs est parfois obligatoire dans la balise ouvrante d’un élément. Par ex-
emple, la balise T img U qui permet d’inclure des images dans un document HTML/XHTML doit toujours
contenir un attribut src avec l’URL d’une image à inclure et un attribut alt avec un texte d’explication
si l’image ne peut pas être affichée (l’attribut alt est optionnel dans HTML 4.0). Voici un extrait de la
définition des attributs spécifiés pour les éléments de type img dans la DTD XHTML :
<!ATTLIST img src CDATA #REQUIRED
alt CDATA #REQUIRED
height CDATA #IMPLIED
width CDATA #IMPLIED>
Les src et alt sont des attributs obligatoires. Les attributs height et width sont optionnels et
permettent de définir la hauteur et la largeur de l’image (en pixels ou en pourcentage d’espace disponible).
Les éléments de type img peuvent également contenir un attribut ismap dont la présence indique
si l’image contient des zones qui servent comme boutons pour des liens vers d’autres pages XHTML.
162 CHAPTER 4. PRODUCTION DE DOCUMENTS XML
Dans HTML 4.0 il suffit d’inclure cet attribut dans la balise ouvrante sans définir une valeur. Ceci n’est
pas permis dans XHTML qui impose la définition d’une valeur pour chaque attribut. Pour cela, XHTML
définit ismap comme un attribut optionnel qui peut prendre comme seule valeur le mot ismap :
Des exemples de balises T img U ouvrantes valides et non-valides sont donnés dans le tableau 4.1.
Balise Valide ?
<img src="lo.gif" valide
alt="logo">
<img src="lo.gif" alt="logo" valide
width="100">
<img src="lo.gif" alt="logo" valide
ismap="ismap">
<img> invalide : les attributs src et alt man-
quent
<img src="lo.gif" alt="logo" invalide : "toto" n’est pas permis
ismap="toto" comme valeur de ismap
L’attribut ismap est un exemple trivial d’un attribut de type énumération : au lieu de spécifier une
seule valeur, il est également possible de spécifier un ensemble de valeurs possibles séparées par le symbole
« | ». Un exemple est l’attribut type associé aux éléments de type button ( boutons d’un formulaire
XHTML) :
Après #IMPLIED et #REQUIRED, un troisième type d’utilisation d’un attribut est défini par le mot
réservé #FIXED. IL est assez rare et permet de définir des attributs avec une valeur constante : la décla-
ration de l’attribut doit contenir une valeur par défaut et toutes les occurrences de cet attribut, optionnel,
doivent avoir la même valeur. L’attribut xmlns défini pour l’élément racine d’un document XHTML est
un exemple d’un attribut fixe :
Cet attribut définit l’espace de nommage des éléments XHTML Et ne peut pas être changé. Des exem-
ples de balise T html U ouvrantes valides et non-valides sont donnés dans le tableau 4.2.
Balise Valide ?
<html> valide
<html xmlns="http://www.w3.org/1999/xhtml"> valide
<html xmlns="http://foo.bar/xhtml"> invalide : espace de noms
différent de la valeur fixée
Types d’attributs
À part CDATA il existe neuf autres types d’attributs qui sont résumés dans le tableau 4.3.
4.1. DÉFINITION DE TYPES DE DOCUMENTS : DTD 163
Identification d’éléments
Les attributs de type ID permettent d’identifier les éléments dans un document XML. XHTML définit un
attribut identificateur pour un grand nombre de types d’éléments. Par exemple, la définition de l’élément
table contient un attribut id :
Attention, il ne faut pas confondre le nom de l’attribut (id) avec son type (ID) : c’est uniquement le
type qui décide si un attribut permet d’identifier un élément dans un document XML. En d’autres termes,
pour connaître les attributs identificateurs d’un document XML, l’inspection de sa DTD est obligatoire.
Les contraintes imposées pour l’utilisation des attributs identificateurs sont les suivants :
2. Il ne doit pas y avoir plusieurs éléments avec la même valeur pour un attribut déclaré comme ID dans
un même document XML. Il est important à noter que cette contrainte porte sur tous les éléments
d’un document XML indépendamment de leur type. Ainsi, même si deux éléments sont de types
différents, il ne doivent pas avoir le même identificateur (on dit que les identificateurs ne sont pas
typés).
Remarque : L’introduction d’attributs identificateurs est nouveau dans la « version » XHTML de HTML 4.0
qui avait une notion beaucoup plus faible d’identification, définie uniquement pour les éléments de type a
(ancres) identifiées par l’attribut name.
On n’est pas obligé de définir un identificateur pour chaque élément dans un document XML. D’une
manière générale, on ne définit des identificateurs que pour les éléments qui sont référencés par d’autres
éléments. Ce référencement est possible grâce à des attributs de type IDREF et IDREFS. Un exemple d’un
tel attribut est for défini pour les éléments de type label :
Ce type d’élément permet de définir une légende pour d’autres éléments dans un formulaire comme
button, input ou select. Voici un exemple extrait de la recommandation XHTML :
On a essayé de répondre à ces limites par la définition d’un langage de définition de « schémas XML »,
qui est une récente (mai 2001) recommandation W3C pour la définition de la structure de documents
XML. Un schéma XML est un document XML, il sépare la notion de balise de la structure d’un élément et
introduit une multitude de type atomiques (une cinquantaine) nouveaux pour la représentation de données.
De plus, la recommandation XML Schéma introduit des notions de modélisation objet comme l’héritage,
l’extension et la redéfinition de types.
XML Schéma est un pas vers une meilleure utilisation de XML pour l’échange d’informations, car il
permet une description plus détaillée et plus précise de la structure des données échangées. Néanmoins,
pour le moment et à cause de leur jeunesse, la richesse des schémas XML n’est pas encore exploitable
4.2. SITE WEB: HTML 165
pour la programmation XSLT. XML schéma est au niveau de la spécification formelle, et il n’existe pas
encore d’interface ou de modèle de programmation standard - analogue à DOM pour le couple XML/DTD
- permettant de développer des apllications.
XHTML
Ces différences entre les « versions SGML » de HTML et XML on été identifiées et éliminées en 2000
dans la dernière version de HTML 4.0, conforme à la syntaxe XML, et appelée XHTML. Un avantage
important de cette évolution est la possibilité d’exploiter les documents XHTML (HTML-4.0) par tous les
outils XML actuels. En particulier, il devient possible de produire et même de transformer des documents
XHTML avec XSLT. Voici la version XHTML de la page HTML ExHTML4.html :
Exemple 61 ExXHTML.xml : Page XHTML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Exemple de Page XHTML</title>
</head>
<body>
<p>Paragraphe 1</p>
<p><img src="image.gif" alt="une image" width="3Opt"
ismap="ismap"/>
</p>
</body>
</html>
Si on compare le contenu des éléments de type html dans les fichiers ExHTML4.html et ExXHTML.xml,
on constate que le deuxième fichier respecte les contraintes imposées par la syntaxe XML :
S toutes les balises d’éléments (vides et non-vides) sont fermées ;
S les noms des balises et des attributs sont tous écrits en minuscules ;
S tous les attributs ont une valeur entourée par des guillemets ou des apostrophes simples.
Types de Base
En plus des différents types d’attributs définis dans la recommandation XML (CDATA, ID, IDREF, NMTO-
KEN, . . . ), XHTML spécifie quelques dizaines de types d’attributs supplémentaires avec des significations
spécifiques. Tous ces types d’attributs sont définis comme entités paramètres référencées dans la définition
d’un attribut de type correspondant. Par exemple, pour distinguer les chaînes de caractères qui correspon-
dent à des URI (Uniform Resource Identifier), la DTD définit une entité URI :
<!ENTITY % URI "CDATA">
<!-- a Uniform Resource Identifier, see [RFC2396] -->
Une DTD ne permet pas d’imposer les contraintes syntaxiques spécifiques aux URI et la DTD donne
uniquement une référence vers le document de spécification correspondant (RFC2396) dans un commen-
taire. Cet entité est ensuite référencée dans la définition d’un attribut qui accepte une URI comme valeur.
Par exemple, l’attribut href des éléments de type a prend comme valeur l’URI de la ressource qui corre-
spond à la destination d’un lien HTML :
4.2. SITE WEB: HTML 167
À part title, le type meta est le plus utilisé dans l’entête d’un document HTML. Ces éléments
sont vides et permettent de donner des informations supplémentaires sur le contenu du document
sous forme de valeurs d’attributs :
Les deux attributs name et content peuvent être utilisés librement pour donner des informations
« meta » sur le contenu du document. Par exemple, la balise T name U permet d’indiquer l’auteur
d’un document et quelques mots-clés qui peuvent être exploités pour l’indexation par un moteur de
recherche :
<META name="author"
content="L’épée de Bois"/>
<META name="keywords" lang="en-us"
content="movie theatre, cinema, paris"/>
<META name="keywords" lang="fr"
content="cinéma, film, paris"/>
Élément contenu body : Les éléments de type body ont un contenu mixte (du texte mélangé avec d’autres
éléments) défini par l’entité Flow :
On peut distinguer entre les éléments « container » (spécifiés par l’entité block) qui acceptent des
sous-éléments, les éléments « feuilles » (spécifiés par l’entité inline) qui n’acceptent que du texte
et ceux qui peuvent être « container » et « feuilles ».
Le fichier ExXHTMLCompl.html contient les balises les plus courantes dans une page XHTML (HTML),
à l’exception des tableaux (voir page 154), et des formulaires. Nous laissons également de côté la construc-
tion de pages avec cadres (frames).
<p>Paragraphe 2:
Ligne1<br/>Ligne2<hr/>Ligne3
</p>
<p>Paragraphe 3:
<pre>
Texte
formatté
</pre>
Texte <b>gras</b>, <i>italique</i> et <b><i>italique et gras</i></b>,
<font size="-2" color="blue">bleu et petit</font>
</p>
<p>Paragraphe 4:
Une liste
<ul>
<li>Une image:
<img src="Vertigo.gif" alt="une image" width="30" align="bottom"/>
</li>
<li>Une autre liste:
<ol><li>item 1</li><li>item 2</li></ol>
</ul>
<a href="Vertigo.xml">Lien</a> vers la page Vertigo.xml
</p>
</body>
</html>
La figure 4.4 montre l’affichage de ce fichier avec un navigateur XHTML. En occurrence il s’agit de la
version 0.9.2 du navigateur Mozilla, lui-même basé sur Netscape. Tous les navigateurs actuels ne sont pas
capables d’afficher du XHTML.
– la balise h p i est utilisée pour définir quatre paragraphes – les auteurs de pages HTML remar-
querons que ces balise sont fermées.
– les balises h br i et h hr i séparent deux lignes (la deuxième balise ajoute un séparateur sous
forme d’un trait entre les deux lignes) : les fins de lignes dans le texte n’ont pas d’influence sur
sa présentation.
g pendant l’affichage, les espaces sont généralement transformés en un seul caractère blanc. Les
seules exceptions sont les espaces dans le contenu de la balise h pre i qui ne sont pas modifiés. Si
on regarde la définition des attributs de cet élément :
<!ATTLIST pre
%attrs;
xml:space (preserve) #FIXED ’preserve’>
on peut observer qu’il définit un attribut xml:space dont l’unique valeur doit être égale à pre-
serve.
g taille de police : le texte entre les h h1 i et h h2 i a une taille supérieure au texte normal : HTML
définit six niveaux d’agrandissement avec six types de balises ( h h1 i , h h2 ijh h3 i , h h4 i ,
h h5 i , h h6 i ) où la balise h h1 i correspond à l’agrandissement le plus important.
170 CHAPTER 4. PRODUCTION DE DOCUMENTS XML
(l’expérience montre que les logiciels qui sont distribués à un très grand public, ce qui est le cas pour les
navigateurs Web qui sont utilisés par tous les Internautes, se renouvellent lentement). Toutefois, la création
d’un document HTML version 4.0 ou antérieure à partir d’un document XML n’est pas si évidente avec
une feuille de style XSLT. Rappelons qu’un programme XSLT est lui-même un document XML qui doit
respecter les contraintes syntaxiques du langage. Ainsi, la règle suivante, qui remplace les éléments de type
LOGO par un élément HTML vide n’est pas correcte :
<xsl:template match=’LOGO’>
<br><img SRC="image.gif" width=30pt ismap>
</xsl:template>
Inversement, la règle suivante est correcte mais ne produit pas une balise conforme à HTML 4.0 (les
balise h br i et h img i définissent des éléments vides et ne doivent pas être fermés) :
<xsl:template match=’LOGO’>
<br /><img src="image.gif" alt="Affiche du Film" width="3Opt"
ismap="ismap"/>
</xsl:template>
Pour résoudre ce dilemme, XSLT définit l’instruction xsl:output qui permet de spécifier un for-
mattage à posteriori du résultat de la transformation. La méthode qui nous intéresse ici est html, qui traduit
le résultat XHTML en document HTML. Ainsi, en ajoutant l’instruction xsl:output method="html"/>
au début de la feuille de style contenant la deuxième règle, les éléments de type LOGO sont remplacés par
la balise h IMG src=’logo-small.gif’ i .
<xsl:template match="FILM">
<h1><xsl:value-of select="TITRE"/></h1>
<xsl:value-of select="GENRE"/>,
<xsl:value-of select="PAYS"/>,
<xsl:value-of select="ANNEE"/>, de
<xsl:value-of select="AUTEUR"/>
</hr>
<xsl:value-of select="RESUME"/>
</xsl:template>
On peut observer que cette règle affiche la valeur de tous les fils de l’élément FILM. Une application
à d’autres éléments du même type, sous-entend qu’on connaît pour tous les films leur titre, leur auteur,
l’année, le genre, le pays et un résumé. En d’autres terme, l’auteur de cet règle suppose qu’elle s’applique
uniquement à des films qui sont valides par rapport à la DTD Film.dtd suivante :
Exemple 63 Film.dtd : DTD pour les films
Remarque : Le langage XSLT est très complexe et il n’est pas possible de vérifier pour un programme
XSLT quelconque qu’il transforme tous les document valides par rapport à une DTD vers des documents
d’une autre DTD.
Remarque : Bien que la référence vers le film contienne l’identificateur d’un élément de type FILM,
elle n’est pas définie comme attribut de type IDREF. Une telle définition aurait comme conséquence
l’obligation de la présence des films dans le même document (ce qui n’est pas le cas). Nous allons revenir
sur cette question plus loin.
Dans cette DTD, on peut également voir la présence d’éléments optionnels (REMARQUE) et d’éléments
répétés (SEANCE). Prenons une première règle de transformation définie pour les éléments de type SALLE
et SEANCE :
4.2. SITE WEB: HTML 173
<xsl:template match="SALLE">
<h2>Salle No <xsl:value-of select="@NO"/></h2>
<h3>Séances</h3>
<ol>
<xsl:apply-template select="SEANCES/SEANCE" />
</ol>
<xsl:for-each select="REMARQUE">
Remarque: <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
<xsl:template match="SEANCE">
<li><xsl:value-of select="."/></li>
</xsl:template>
Cette règle crée une liste de type ol qui contient le résultat de la transformation des éléments de type
SEANCE : la règle correspondante n’affiche que l’heure de la séance. Pour faire cette transformation, on
aurait également pu construire une boucle xsl:for-each sur toutes les séances, comme pour l’affichage
de la remarque (si elle est présente).
Si on compare à nouveau ce programme avec la DTD des salles (Salle.dtd), on constate que ces deux
« boucles » (rappelons que l’application d’une règle par xsl:template peut toujours être traduite par
une boucle xsl:for-each) correspondent exactement aux éléments répétés (les séances) et à l’élément
optionnel (la remarque).
Cette page peut être transformée très facilement en une page HTML qui contient des liens vers les ressources
de tous les films et ceci grâce à l’attribut HREF. Nous montrons uniquement la règle de transformation des
éléments de type FILM :
<xsl:template match="FILM">
<tr><td>
<a href=’{@HREF}’><xsl:value-of select="TITRE"/></a>
</td></tr>
</xsl:template>
Chaque élément h FILM i est traduit en une ligne dans un tableau. Par exemple, la traduction du
premier élément donne le fragment HTML suivant comme résultat :
<tr>
<td><a href="Vertigo.xml">Vertigo</a></td>
</tr>
Le résultat de la transformation complète du fichier MesFilms.xml est montré dans la figure 4.5.
Maintenant nous voudrions créer ce type de lien entre la page HTML qui correspond à une salle et
les pages HTML qui correspondent aux films projetés dans cette salle. C’est un peu plus compliqué,
car les pages XML concernant les salles ne contiennent que des références vers des films, mais n’ont
pas l’information concernant les ressources : il n’est pas possible de déduire le nom de la ressource, par
exemple Vertigo.xml à partir de la référence du film dans Salle1.xml.
174 CHAPTER 4. PRODUCTION DE DOCUMENTS XML
Pour pouvoir créer ces liens, on est obligé d’accéder en même temps au fichier Salle1.xml et au fichier
MesFilms.xml dans le programme de transformation. C’est possible en utilisant la fonction document() du
langage XPath, qui permet de choisir une ressource comme nouvelle racine pour l’évaluation des étapes
dans une expression XPath. Voici un exemple d’utilisation de cette fonction dans la règle de transformation
d’éléments de type SALLE qui se trouvent dans le document Salle1.xml (ou n’importe quel autre document
satisfaisant la DTD Salle.dtd) :
<xsl:template match="SALLE">
<h2>Salle No <xsl:value-of select="@NO"/></h2>
Film:
<xsl:variable name="filmref" select=’@FILMREF’ />
<xsl:variable name="film"
select="document(’MesFilms.xml’)/LISTEFILM/FILM[@FILMREF=$filmref]" />
<xsl:element name=’a’>
<xsl:attribute name=’href’>
<xsl:value-of select=’$film/@HREF’/>
</xsl:attribute>
<xsl:value-of select="$film/TITRE"/>
</xsl:element>
</xsl:template>
Cette règle définit deux variables. La première, filmref, contient la référence du film dont on cherche le
titre et la ressource. La deuxième trouve ce film dans le document MesFilms.xml par une expression XPath
qui :
g utilise le document MesFilms.xml comme racine ;
g trouve tous les éléments de type FILM dans ce document ;
g filtre parmi ces éléments ceux qui ont un attribut FILMREF dont la valeur est identique à la valeur
de la variable filmref.
Cet élément – il n’y en a qu’un si on définit l’attribut FILMREF comme un identificateur d’éléments
dans le document MesFilms.xml – est ensuite utilisé pour construire un élément de type a qui contient :
g un attribut href dont la valeur est la ressource du film : $film/@HREF ;
g le titre du film : $film/TITRE.
4.2. SITE WEB: HTML 175
<xsl:template name="createLinkFilm">
<xsl:param name="filmref" select="inconnu"/>
<xsl:variable name="film"
select="document(’MesFilms.xml’)/LISTEFILM/FILM[@FILMREF=$filmref]" />
<xsl:element name=’a’>
<xsl:attribute name=’href’>
<xsl:value-of select=’$film/@HREF’/>
</xsl:attribute>
<xsl:value-of select="$film/TITRE"/>
</xsl:element>
</xsl:template>
Cette règle prend un identificateur de film comme paramètre et crée un lien vers la ressource correspon-
dante.
Il est possible que cette ressource n’existe pas pour tous les films, c’est-à-dire qu’il n’existe pas
d’élément de type FILM dans le dictionnaire pour la référence donnée. Dans ce cas, on peut définir la
règle suivante, qui retourne la valeur 1 si la ressource existe, et la valeur 0 si elle n’existe pas :
<xsl:template name="testFilm">
<xsl:param name="filmref" select="inconnu"/>
<xsl:choose>
<xsl:when
test="document(’MesFilms.xml’)/LISTEFILM/FILM[@FILMREF=$filmref]">
<xsl:text>1</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>0</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
En utilisant ces deux règles, on peut redéfinir la règle pour les salles de cinéma de la façon suivante :
<xsl:template match="SALLE">
<h2>Salle No <xsl:value-of select="@NO"/></h2>
<xsl:variable name="exists">
<xsl:call-template name="testFilm">
<xsl:with-param name="filmref" select="@FILMREF"/>
176 CHAPTER 4. PRODUCTION DE DOCUMENTS XML
</xsl:call-template>
</xsl:variable>
<xsl:if test="$exists=1">
<h3>Film:
<xsl:call-template name="createLinkFilm">
<xsl:with-param name="filmref" select="@FILMREF"/>
</xsl:call-template>
</h3>
</xsl:if>
</xsl:template>
La déclaration de DTD contient une référence vers un fichier externe (Cinema.dtd) et la déclaration de
deux entités externes salle1 et salle2 qui correspondent aux éléments de type SALLE décrivant les
deux salles de cinémas, leurs séances et les films projetés. La DTD contient elle-même une référence vers
la DTD des salles :
Exemple 68 Cinema.dtd : DTD pour les cinémas
<!ELEMENT CINEMA (NOM, ADRESSE, METRO, (SALLE)*) >
<!ELEMENT NOM (#PCDATA) >
<!ELEMENT ADRESSE (#PCDATA) >
<!ELEMENT METRO (#PCDATA) >
<!ENTITY % salle-dtd SYSTEM ’Salle.dtd’>
%salle-dtd;
4.3. PRÉSENTATIONS MULTIMÉDIA: SMIL 177
On voit, par cet exemple, comment on peut composer en même temps des documents XML et la
description de leurs structure en utilisant les entités. Cette composition peut même être propagée au
niveau du traitement des données – les programmes XSLT – en utilisant les instructions xsl:import
ou xsl:include décrites dans le chapitre 3 page 115. Voici un programme de transformation de ciné-
mas qui réutilise les règles définies dans le fichier Salle.xsl :
Exemple 69 Cinema.xsl : DTD pour les cinémas
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="Salle.xsl"/>
<xsl:output method="html" encoding="iso-8859-1"/>
<xsl:template match="/">
<html>
<head><title>Programme de <xsl:value-of select="CINEMA/NOM"/></title>
</head>
<body>
<xsl:apply-templates select="CINEMA"/>
</body>
</html>
</xsl:template>
<xsl:template match="CINEMA">
<h1><i><xsl:value-of select="NOM"/> </i> </h1><hr/>
<xsl:value-of select="ADRESSE"/>,
<i>Métro: </i> <xsl:value-of select="METRO"/>
<hr/>
<xsl:apply-templates select="SALLE"/>
</xsl:template>
</xsl:stylesheet>
proposition).
Le fichier ExSimple.smil est un document multimédia intégrant trois composants :
1. un fichier audio Sound.wav qui dure 2.1 secondes ;
2. l’affiche française du film Vertigo stockée dans le fichier Vertigo.png de 170x240 pixels ;
3. et un fichier texte TexteVertigo.txt.
Exemple 70 ExSimple.smil : Un fichier SMIL simple
<smil>
<head>
k
<meta name="title" content="Exemple simple d’un fichier SMIL" />
<meta name="author" content=" c 2001 Sallesenligne.com" />
<layout>
<root-layout width="450" height="600" />
<region id="reg_img_grand" width="400" height="300" left="25" top="25" fit="meet" />
<region id="reg_img_petit" width="200" height="125" left="125" top="75" fit="meet" />
<region id="reg_texte" width="350" height="325" left="50" top="250" />
</layout>
</head>
<body>
<seq>
<par endsync="first" id="page1" >
<audio src="Sound.wav" />
<a href="#page2" >
<img src="Vertigo.png" region="reg_img_grand" />
</a>
</par>
<par id="page2" dur="30s" >
<text src="Vertigo-Resume.txt" region="reg_texte" />
<img src="Vertigo.png" region="reg_img_petit" />
</par>
</seq>
</body>
</smil>
reg_img_grand
reg_img_petit
600 pixels
reg_texte
450 pixels
Média Utilisation
text composant textuel
image image statique
audio composant sonore
video séquence vidéo
textstream texte à déroulement automatique
animation composant animation
ref composant d’un autre type
Le texte TexteVertigo.txt est placé dans la région reg_texte et le fichier audio Sound.wav n’a
pas de placement spatial (la version 2.0 propose la possibilité de contrôler le volume pendant l’écoute d’un
fichier audio mais nous n’avons pas trouvé d’indication sur la possibilité de contrôler le volume sur les
différents haut-parleurs dans un environnement multi-phonique).
L’arbre DOM du contenu (élément racine) du document est donné dans la figure 4.7.
Element
seq
Element
par
Le positionnement temporel des composants du document est décrit en utilisant une combinaison de
quatre modèles de synchronisation proposés par SMIL :
1. Synchronisation par composition : deux types d’éléments de « synchronisation » permettent de re-
grouper différents composants en séquences (seq) et en « groupes parallèles » (par) qui se traduisent
respectivement par un affichage séquentiel ou parallèle des composants.
Par exemple, le fichier ExSimple.smil définit deux groupes parallèles (éléments de type par avec
les identificateurs page1 et page2). Le premier groupe (page1) joue le son dans le fichier
Sound.wav et affiche en même temps l’image Vertigo.png. Le groupe page2 affiche en
même temps l’image et le texte dans le fichier TexteVertigo.txt.
La combinaison répétée d’éléments de synchronisation permet de constituer des composants multi-
média de plus en plus complexes : le sous-élément seq définit une séquence qui affiche d’abord le
groupe page1 suivi du groupe page2.
2. Synchronisation par événement interne : le déroulement temporel de l’affichage de différents com-
posants est contrôlé par des événements internes comme par exemple le début ou la fin de l’affichage
d’un autre composant. Ce type de synchronisation est implanté sous forme de plusieurs attributs qui
peuvent être utilisés dans les éléments de placement et les éléments de synchronisation.
Par exemple, l’attribut endsync dans le groupe parallèle page1 du document ExSimple.smil syn-
chronise la fin de l’affichage de ce groupe par la fin de son premier sous-élément (l’affichage de la
page 1 dure aussi longtemps que le son dans le fichier Sound.wav).
4.3. PRÉSENTATIONS MULTIMÉDIA: SMIL 181
3. Synchronisation par horloge : une horloge permet de spécifier le début, la fin et la durée de l’affichage
d’un objet. Par exemple, la durée de la deuxième page est fixée à 10 secondes par l’attribut dur.
4. Synchronisation par l’utilisateur : l’utilisateur peut également intervenir dans le déroulement d’un
document SMIL. Cette intervention correspond à l’activation d’un lien hypertexte vers un composant
du même document ou d’une autre ressource Web. Cette ressource peut évidemment être aussi un
document SMIL.
Dans le document ExSimple.smil, l’élément de type a crée un lien entre l’image affichée et le groupe
page2. En « cliquant » sur l’image, l’utilisateur peut passer à la deuxième page avant la fin du son
stocké dans le fichier Sound.wav.
La combinaison de ces quatre modèles de synchronisation permet de spécifier des documents multimé-
dia très complexes. Avant de créer un document SMIL, il est donc conseillé de spécifier un ou plusieurs
scénarios d’affichage. S SMIL n’est peut-être pas le meilleur langage pour faire une telle spécification, nos
quelques expériences avec ce langage nous ont montré que les éléments de synchronisation seq et par
fournissent une façon relativement simple et déclarative de création de document multimédia.
Dans la suite, nous donnons un petit résumé de la DTD SMIL et montrons comment on peut exploiter
un ou plusieurs documents XML pour la création de documents SMIL.
Entête : Mise-en-page
L’entête contient zéro ou plusieurs éléments h meta i et au moins un élément de type layout ou de type
switch :
<!ELEMENT head (meta*,((layout|switch), meta*))?>
Les éléments meta sont définis et utilisés de la même manière que les éléments du même type dans
HTML.
Balise h layout i : Cette balise permet de faire la mise en page de la présentation SMIL. La DTD
SMIL n’est pas très explicite sur le contenu un élément de ce type :
<!ELEMENT layout ANY>
On peut dire sans trop s’aventurer qu’un élément de type layout contient généralement un élément
de type root-layout, qui permet de spécifier la mise en page globale de la fenêtre de présentation, et
un ou plusieurs éléments de type region. Tous ces éléments sont vides et utilisent des attributs pour
spécifier les propriétés de la fenêtre et des différentes région qu’elle contient.
Parmi ces attributs, on trouve les attributs height, width et background-color qui sont rassem-
blés par une entité générale nommé viewport-attrs :
Ainsi la balise h root-layout i est définie de la manière suivante (pour simplifier la présentation
nous ne présentons qu’une partie des attributs définis dans la DTD SMIL) :
<!ELEMENT root-layout EMPTY>
<!ATTLIST root-layout %viewport-attrs; >
Par exemple l’élément suivant définit d’une manière conforme à la DTD SMIL une fenêtre de présen-
tation de 300x400 pixels :
<root-layout width="300" height="400"/>
Les éléments h region i , qui sont également vides, permettent de définir la dimension et la position
de différentes régions dans la fenêtre de présentation :
<!ELEMENT region EMPTY>
<!ATTLIST region
id ID #IMPLIED
%viewport-attrs;
left CDATA "0"
top CDATA "0"
z-index CDATA "0"
fit (hidden|fill|meet|scroll|slice) "hidden"
>
On voit que chaque région peut être identifiée par un attribut id, ce qui sera utile pour relier plus tard
un ou plusieurs composants à une région. La dimension d’une région est spécifiée par les mêmes attributs
que la dimension de la fenêtre de présentation. La position d’une région est caractérisée par trois attributs
qui correspondent aux trois dimensions d’affichage :
1. l’attribut left permet de spécifier le nombre de pixels entre la région et le bord gauche de la fenêtre.
Il peut également spécifier un décalage en pourcentage de la largeur de la fenêtre.
2. l’attribut top permet de spécifier le nombre de pixels entre la région et le bord supérieur de la
fenêtre. Il est possible également de spécifier un décalage en pourcentage de la largeur de la fenêtre.
3. l’attribut z-index définit la « troisième » dimension. Quand deux objets sont affiché dans deux
régions qui se recouvrent, il faut décider lequel des deux objets sera complètement visible et lequel
sera partiellement couvert par l’autre. Ce conflit est résolu de la manière suivante :
g si les deux régions ont une valeur identique pour l’attribut z-index, ce sera l’objet dont
l’affichage a été déclenché le dernier qui aura la priorité. Quand les deux objets sont affichés en
même temps, c’est le dernier dans l’ordre du document SMIL qui sera affiché complètement.
g sinon c’est la région avec la valeur supérieure pour l’attribut z-index qui recouvrera l’autre
région.
Exemples :
g La dimension de la région reg_texte1 est de 300x50 pixels, elle est déplacée de 50 pixels vers la
droite et 120 pixels vers le bas.
<region id="reg_texte1"
width="200" height="50"
left="50" top="120"
z-index="1"/>
g La région reg_img_grand fait 300x400 pixels et le contenu affiché sera redimensionné sans dé-
formation. Cette région se trouve « en dessous » de la région précédente (z-index est égal à 0 par
défaut).
<region id="reg_img_grand"
width="300" height="400"
fit="meet"/>
4.3. PRÉSENTATIONS MULTIMÉDIA: SMIL 183
Éléments de positionnement
Le rôle principal des éléments de positionnement est de placer le contenu des différents composants du
document dans les différentes régions spécifiées dans l’en-tête et de décrire leur comportement pendant le
« déroulement » du document.
Les attributs des éléments de positionnements sont définis dans l’entité suivante :
<!ENTITY % mo-attributes "
id ID #IMPLIED
region IDREF #IMPLIED
src CDATA #IMPLIED
dur CDATA #IMPLIED
repeat CDATA ’1’
fill (remove|freeze) ’remove’
begin CDATA #IMPLIED
end CDATA #IMPLIED
">
g src : spécifie la ressource (le contenu) à afficher dans cet élément de placement (un ressource peut
être affichée à différentes endroit en même temps) ;
g id : permet d’identifier l’élément de positionnement ;
g region : spécifie la région où le contenu sera affiché ;
g repeat : le nombre de répétitions de l’affichage (par exemple pour un fichier audio, qui peut être
joué en boucle) ;
g fill : le mode de remplissage de la région quand l’affichage de la ressource est terminée . La valeur
par défaut est remove, ce qui signifie que la région efface le contenu et reprend sa couleur de fond.
La valeur freeze indique que le contenu reste affiché (par exemple la dernière image dans une
vidéo ou une séquence d’images statiques).
g dur : la durée d’affichage ;
g begin : le début d’affichage ;
184 CHAPTER 4. PRODUCTION DE DOCUMENTS XML
Exemple 71 g Le fichier texte Alien-Info.txt est affiché dans la région reg_texte2 pendant
5 secondes :
L’affichage des composants qui correspondent à un média continu (audio, animation, textstream, vidéo)
peut être contrôlé par deux attributs supplémentaires qui permettent de choisir un intervalle dans le contenu
du composant à afficher :
<!ENTITY % clip-attrs "
clip-begin CDATA #IMPLIED
clip-end CDATA #IMPLIED
">
Éléments de synchronisation
Les balises h par i et h seq i ont le même modèle de contenu que l’élément h body i , mais permettent
l’utilisation d’attributs supplémentaires pour la synchronisation :
<!ATTLIST par id ID #IMPLIED
endsync CDATA "last"
dur CDATA #IMPLIED
repeat CDATA "1"
region IDREF #IMPLIED
begin CDATA #IMPLIED
end CDATA #IMPLIED
>
La signification de ces attributs est la suivante :
g endsync : cet attribut peut prendre comme valeur les chaînes first et last ou un identificateur
d’un élément fils du groupe ce qui indique respectivement que le groupe se termine à la fin du premier
fils, du dernier ou de celui référencé. Cela correspond à une synchronisation par événement.
g dur : spécifie la durée du groupe en temps réel (en heures, minutes et/ou secondes). Cela correspond
à une synchronisation par horloge ;
g repeat : indique combien de fois le groupe doit être affiché (boucle) ;
4.3. PRÉSENTATIONS MULTIMÉDIA: SMIL 185
Exemple 73 Le groupe parallèle de deux éléments h audio i et h img i se termine à la fin de l’affichage
du premier élément (l’image sera affichée pour la durée de l’extrait sonore dans Sound.wav) :
<par endsync="first">
<audio src="Sound.wav"/>
<img src="Alien.png" region="reg_img_grand"/>
</par>
Element
par
<seq>
<par endsync="first">
<audio src="Sound.wav"/>
<a href="#page2">
<img src="Vertigo.png" region="reg_img_grand"/>
</a>
</par>
<par id="page2">
<text src="Vertigo-Title.txt" region="reg_texte1" fill="freeze"/>
<text src="Vertigo-Info.txt" region="reg_texte2"/>
<text src="Vertigo-Resume.txt" region="reg_texte3" begin="2s"/>
<seq>
<img src="IMG/Vertigo1.png" region="reg_img_petit" dur="5s"/>
<img src="IMG/Vertigo2.png" region="reg_img_petit" dur="5s"/>
</seq>
</par>
</seq>
186 CHAPTER 4. PRODUCTION DE DOCUMENTS XML
Element
seq
Element Element
par par
Cet extrait définit une séquence de deux groupes parallèles. Le premier groupe affiche – pour la durée
du sons dans le fichier Sound.wav l’image Vertigo.png, tandis que le deuxième affiche trois fichiers
de texte en même temps qu’une séquence d’images.
La figure 4.10 montre trois copies d’écran dans l’ordre chronologique du déroulement de cette séquence.
On peut remarquer que le titre Vertigo-Title.txt est resté affiché à la fin à cause de l’attribut fill
qui est égal à freeze dans son élément de placement.
Exemple 75 Par exemple, l’élément h switch i suivant prend en compte la taille de l’écran pour choisir
entre trois versions à différentes résolutions d’une même image ;
4.3. PRÉSENTATIONS MULTIMÉDIA: SMIL 187
<switch>
<par system-screen-size="640x480">
<img src="img_faible.gif" region="reg_img" />
</par>
<par system-screen-size="800x600">
<img src="img_moyen.gif" region="reg_img" />
</par>
<par system-screen-size="1024x768">
<img src="img_haut.gif" region="reg_img" />
</par>
<text src="sorry.txt" region="reg_text_msg" />
</switch>
SMIL fournit une multitude d’autres mécanismes pour la création de documents multimédia. Il est
possible de créer et de sélectionner des sous-régions et intervalles temporels sur/pendant lesquels des liens
vers d’autres ressources sont actifs. On peut également définir différents liens sur différentes régions d’une
image, ou des liens qui sont accessibles seulement pendant un certain temps.
Pour illustrer ces différents aspects, nous allons montrer comment on peut générer une présentation
SMIL à partir des documents XML sur les films projetés par le cinéma L’Épée de Bois. Un document sur
le film Sleepy Hollow est le fichier 76 ;
Ces trois contenus textuels sont affichés dans trois régions différentes en même temps qu’une séquence
de quatre images. Avant de voir comment ce document et tous ces composants ont été générés, regardons
de plus près le contenu de chaque fichier texte.
Voici le fichier qui contient le titre du film :
Ce fichier ne contient pas uniquement le titre, mais est lui-même un fichier XML. Le format utilisé
dans ce fichier s’appelle RealText et est proposé par la société RealNetworks.
4.3. PRÉSENTATIONS MULTIMÉDIA: SMIL 189
Remarque : SMIL ne spécifie pas le format du contenu textuel des fichiers et le format RealText est un
format spécifiquement défini pour une gamme de produits d’une société.
Comm HTML, les fichiers RealText permettent de spécifier la présentation du texte en utilisant des
balises. Parmi les balises les plus importantes, nous pouvons citer :
g h window i : cette balise permet, entre autre, de spécifier le type d’affichage utilisé (attribut type),
les dimensions de la fenêtre de texte (la taille de cette fenêtre doit être inférieure ou égale à aux
dimensions de la région SMIL dans laquelle elle est affiché), la couleur du fond et des liens, etc...
g h font i : permet de spécifier des caractéristiques de la police utilisée pour le texte qu’elle contient
(couleur, taille, etc.).
g h ai : permet de créer des liens vers d’autres ressources, comme l’élément du même type en HTML.
D’autres balises, reprises de la DTD XHTML, comme h center i , h br i et h p i permettent de
mettre en page le texte.
Les balises dans le fichier 78 spécifient que le titre du film est affiché dans une fenêtre de 200x50 pixels,
le texte est affiché centré, en rouge avec une police Garamond et une taille de 50 pixels.
Le titre est affiché dans une fenêtre de type marquee, ce qui signifie que le texte se déroule horizon-
talement de droite à gauche. RealText distingue entre cinq type d’affichage d’un texte :
g generic : affichage générique sans paramètres prédéfinis ;
g marquee : le texte se déroule horizontalement (de droite à gauche par défaut) et est positionné au
milieu de la fenêtre ;
g tickertape : le texte se déroule verticalement (de droite à gauche par défaut) et est positionné en haut
ou en bas de la fenêtre ;
g scrollingnews : le texte se déroule verticalement (du bas vers le haut par défaut) ;
g teleprompter : le texte est affiché comme dans l’affichage générique mais le nouveau texte « pousse »
l’ancien vers le haut.
Les informations se déroulent également de droite à gauche. Il contient également un lien vers le fichier Sleepy-
Hollow.xml (on aurait pu évidement choisir n’importe quel URL comme cible de ce lien).
Le résumé se déroule verticalement (scrollingnews) dans une fenêtre de 250x100 pixels en jaune sur un
fond noir :
Exemple 80 Vertigo-Resume.txt : Résumé du film Vertigo
</font>
</window>
<xsl:template match="FILM">
<window type="marquee" height="50" width="200" bgcolor="black">
<font size="50" face="Garamond" color="red">
<xsl:value-of select="TITRE"/>
</font>
4.3. PRÉSENTATIONS MULTIMÉDIA: SMIL 191
</window>
</xsl:template>
Cet règle, qui s’applique aux éléments de type FILM, crée les balises RealTime pour le formattage du
texte et insère le titre du film. Il suffit de créer une feuille de style complète avec cette règle et le résultat
de la transformation XSLT pourra être mis dans le fichier Sleepy-Title.txt.
La règle suivante affiche les informations sur le film :
<xsl:template match="FILM">
<window type="marquee" height="50" width="200" bgcolor="black" link="blue">
<font color="white">
de <xsl:value-of select="AUTEUR"/>,
<xsl:value-of select="GENRE"/>,
<xsl:value-of select="PAYS"/>,
<xsl:value-of select="ANNEE"/>
</font>
</window>
</xsl:template>
Cette règle est correcte mais ne génère pas exactement le contenu du fichier Sleepy-Info.xml plus haut.
Voici le texte qu’elle génère :
Au lieu d’écrire toutes les informations dans une seule ligne, chaque « champ » commence par une nouvelle
ligne. Nous avons vu que ceci n’est pas génant dans l’affichage d’une page HTML par un navigateur qui
remplace tous les espaces par un seul caractère. RealPlayer ne fait pas la même chose : pendant l’affichage
de ce texte les différents champs seront séparés par des espaces plus longs qu’un seul caractère, ce qui n’est
pas un résultat souhaitable. Il existe deux solutions à ce problème :
1. La première est d’écrire la règle de la manière suivante :
<xsl:template match="FILM">
<window type="marquee" height="50" width="200" bgcolor="black" link="blue">
<font color="white">
de <xsl:value-of select="AUTEUR"/>, <xsl:value-of select="GENRE"/>,
<xsl:value-of select="PAYS"/>, <xsl:value-of select="ANNEE"/>
</font>
</window>
</xsl:template>
<xsl:template match="FILM">
<window type="marquee" height="50" width="200" bgcolor="black" link="blue">
192 CHAPTER 4. PRODUCTION DE DOCUMENTS XML
<font color="white">
de <xsl:value-of select="AUTEUR"/><xsl:text>, </xsl:text>
<xsl:value-of select="GENRE"/><xsl:text>, </xsl:text>
<xsl:value-of select="PAYS"/><xsl:text>, </xsl:text>
<xsl:value-of select="ANNEE"/><xsl:text>, </xsl:text>
</font>
</window>
</xsl:template>
Maintenant les espaces qui se trouvent entre les éléments h xsl:text i et h xsl:value i seront
supprimés pendant la sérialisation de l’arbre résultat. Nous allons détailler l’instruction h xsl:text i
dans la section 4.4.2.
Finalement, la dernière règle est la suivante :
<xsl:template match="FILM">
<window type="scrollingnews" height="100" width="250" bgcolor="black">
<font color="yellow">
<xsl:value-of select="RESUME"/>
</font>
</window>
</xsl:template>
En prenant en compte toutes les remarques précédentes, on peut supposer que cette règle est correcte.
Le seul problème est que le résumé contient des caractères latins utilisant le codage ISO-8859-1 et que
le texte généré contient également ce codage. Pour pas avoir des caractères illisibles nous sommes obligés
de définir le codage en utilisant l’instruction xsl:output :
Les attributs de l’instruction saxon:output sont essentiellement les mêmes que les attributs de l’élément
du premier niveau xsl:output.
Après avoir généré les différents composants RealText à partir du contenu du document XML Sleepy-
Hollow.xml, nous voulons maintenant créer le fichier SMIL final qui est composé de quatre autres
composants images qui n’ont pas de relation directe avec le document XML déjà transformé. Afin de
pouvoir utiliser le même programme pour la transformation d’autres fichiers XML sur les films, il n’est
pas possible de spécifier ces ressources images explicitement dans le programmes sous forme d’éléments
littéraux.
Une meilleure solution est de définir un document XML qui contient les paramètres manquants pour
la génération de la présentation finale. Voici un exemple d’un tel document de « paramètres » qui indique
pour chaque film les ressources disponibles :
Le film Sleepy Hollow correspond au troisième film (FILMREF=3) et donne pour ce film un préfixe
pour les noms de fichiers RealText qui seront générés par les règles précédent, une URL pour une ressource
Web à référencer dans la présentation (lien dans le fichier Sleepy-Info.txt) et quatre URL pour récupérer
des photos sur le film. Voici le programme XSL pour la génération du document SMIL :
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<smil>
<head>
<meta name="title" content="Presentation SMIL du film {FILM/TITRE}"/>
<meta name="author" content="(c) 2001 Sallesenligne.com" />
<layout>
<root-layout width="300" height="400" />
<region id="reg_img_grand"
width="300" height="400" fit="meet" />
<region id="reg_img_petit"
width="150" left="80" height="100" top="10" />
<region id="reg_texte1"
width="200" left="50" height="50" top="120" fit="meet"/>
<region id="reg_texte2"
width="200" left="50" height="50" top="180" />
<region id="reg_texte3"
width="250" left="25" height="150" top="240" />
</layout>
</head>
<body>
<xsl:apply-templates select="FILM"/>
</body>
</smil>
</xsl:template>
<xsl:template match="FILM">
<xsl:variable name="filmid" select="@FILMID" />
<xsl:variable name="filmparams">
<xsl:copy-of select="document(’ParamsSMIL.xml’)/SMILPARAMS/PARAM[@FILMREF=$filmid]" />
</xsl:variable>
<par>
<text src="{$filmparams/PARAM/@PREFIX}Title.txt" region="reg_texte1" fill="freeze"/>
<text src="{$filmparams/PARAM/@PREFIX}Info.txt" region="reg_texte2" />
<text src="{$filmparams/PARAM/@PREFIX}Resume.txt" region="reg_texte3" begin="2s"/>
<seq>
<xsl:apply-templates select="$filmparams/PARAM/PHOTOS/PHOTO"/>
</seq>
</par>
</xsl:template>
<xsl:template match="PHOTO">
<img src="{@HREF}" region="reg_img_petit" dur="5s" />
</xsl:template>
</xsl:stylesheet>
g La règle <xsl:template match="/"> génère l’entête du document et applique ensuite la
transformation pour l’élément h FILM i .
g La règle <xsl:template match="FILM"> définit une variable $filmid qui contient l’identificateur
du film et qui est utilisée dans une expression XPath comme valeur de sélection pour trouver l’élément
de type PARAM dans le document ParamsSMI.xml correspondant au film. Une copie de cet élément
4.4. TRAITEMENT DE TEXTE DANS XSLT 195
est ensuite passée comme valeur à une deuxième variable $filmparams. Cette variable est ensuite
utilisée comme racine dans plusieurs expressions XPath pour extraire les paramètres correspondant.
Le résultat de cette règle correspond exactement au contenu de l’élément de type body dans le
fichier SleepyHollow.smil.
Le fichier ParamsSMIL.xml illustre bien l’utilisation de XML comme « format universel » pour la
représentation et l’intégration de données : à partir de ressources XML et non-XML on définit un doc-
ument XML de référence qui relient toutes ces ressources. Ce fichier peut ensuite être utilisé de différentes
manières pour créer des nouvelles ressources comme par exemple un document SMIL.
Dans le jargon de recommandation XLink du W3C, ces références entre les ressources sont appelées
des liens externe par opposition aux liens internes qui sont définis dans le contenu des ressources même :
le Web actuel est basé sur le deuxième type de liens implanté par les éléments h a i de XHTML.
types d’éléments sont spécifiés sous forme d’une liste de noms de balises ou en utilisant le symbole ’*’
pour tous les types d’élément.
Par exemple, l’expression
<xsl:strip-space name="A C"/>
signifie que tous les fils espaces d’éléments de type A et B doivent être supprimés, tandis que l’expression
suivante choisit les fils de tous les éléments :
<xsl:strip-space name="*"/>
Voici un document XML avec des espaces (ces espaces sont uniquement composés de séquences du
caractère « » rendu visible par le symbole « _ ») :
<A>_<B>__<C>_<D>__</D><D>a_</D>_b_</C></B>_c</A>
L’arbre XPath est montré dans la figure 4.12. Les noeuds supprimés par la première expression sont
mis en évidence par un fond gris, tandis que les éléments supprimés en plus par la deuxième expression
sont rayés.
Element
A
Text Element
-
#x20 #x20 C
Text Text
- -
#x20 #x20 a #x20
On voit que seulement les noeuds qui ne contiennent que des caractères blancs sont supprimés.
L’élément xsl:preserve-space a l’effet inverse de l’élément xsl:strip-space, c’est-à-dire
que les fils d’espace blanc sont préservés. Ainsi il est possible de spécifier que le fils de tous les éléments
sauf les éléments h D i doivent être supprimés :
<xsl:strip-space name="*"/>
<xsl:preserve-space name="D"/>
Les conflits possibles qui peuvent être créés par une utilisation simultanée de ces deux éléments sont
résolus de la même manière que les conflits de règles XSLT (chapitre 3).
4.5. RSS ET RDF 197
Attribut xml:space
Les deux éléments de niveau précédent permettent un contrôle du traitement des espaces blanc au niveau
du type d’élément. Un contrôle plus fin est possible dans le contenu même du document à transformer.
Chaque élément XML peut avoir un attribut prédéfini xml:space qui décide si les fils d’espace blanc
doivent être supprimés ou pas. Nous avons déjà illustré l’utilisation de cet attribut dans la balise h pre i
de la DTD XHTML.
L’attribut xml:space peut prendre deux valeurs : default ou preserve. La première valeur in-
dique que c’est l’application – dans notre cas le programme XSLT – qui doit décider. La valeur preserve
ne signifie pas seulement que les fils d’espaces blanc de l’élément mais que tous les descendants d’espace
blanc doivent être préservés jusqu’à l’indication contraire par la valeur default.
En résumé, le processus de décision d’une suppresion des espaces est le suivant. Un élément peut être
supprimé s’il est sélectionné par une expression xsl:strip-space (après résolution des conflits) et si
l’une des deux contraintes suivantes est satisfaite :
g il n’a pas d’élément ancêtre avec un attribut xml:space ou
g l’élément ancêtre avec un attribut xml:space le plus près a comme valeur pour cet attribut la valeur
default.
Exemple 83 Voici un document XML :
<A xml:space="preserve">
<B xml:space="default">
<C> </C>
</B>
<B>
<C> </C>
</B>
</A>
et une expression xsl:strip-space :
<xsl:strip-space elements="B C">
Cette expression demande la suppression de tous les fils de type B et de type C. L’attribut xml:space de
l’élément A indique que les fils d’espace blancs doivent être préservés pour tous les éléments descendants
de cet élément. Cette contrainte est à son tour effacée pour le premier élément de type B et tous ces
descendants. Ainsi, le document précédent peut être considéré comme équivalent au document suivant :
<A>
<B><C/></B>
<B>
<C> </C>
</B>
</A>
Sommaire
199
200 CHAPTER 5. PRODUCTION DE DOCUMENTS PAPIER
Chapter 6
Échange et intégration
Sommaire
201
202 CHAPTER 6. ÉCHANGE ET INTÉGRATION
Chapter 7
Sommaire
7.1 Bases de données et XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
7.1.1 Quelques rappels sur les BD relationnelles . . . . . . . . . . . . . . . . . . . . 204
7.1.2 Documents orientés « texte » et documents orientés « données » . . . . . . . . . 207
7.1.3 Transformation d’une base de données en XML . . . . . . . . . . . . . . . . . 209
7.1.4 Création de la DTD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
7.2 Architectures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
7.2.1 Une classe Java d’exportation XML . . . . . . . . . . . . . . . . . . . . . . . . 218
7.2.2 Architecture Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
7.2.3 Utilisation des Java Server Pages . . . . . . . . . . . . . . . . . . . . . . . . . 225
7.3 XML dynamique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
7.3.1 XSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
7.3.2 XSQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
7.4 Perspectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
Ce chapitre est consacré à la publication de documents incluant des informations issues d’une base de
données. Dans le domaine des sites web, il est désormais classique de construire les pages en intégrant à
du texte fixe des données extraites dynamiquement de la base (la majorité des sites web professionnels sont
d’aileurs gérés de cette manière). Les langages les plus utilisés dans ce type d’application sont ASP, PHP
ou les Java Server Pages (JSP). Tous consistent à placer dans du HTML des instructions qui se connectent
à la base, effectuent des requêtes et mettent en forment le résultat à l’aide de balises HTML.
La justification d’une gestion des données séparée de leur affichage dans une page web est claire :
HTML n’est pas un langage permettant la modélisation, le stockage et l’interrogation de données, et une in-
formation placée « en dur » dans une page HTML ne peut être récupérée pour un usage autre que l’affichage
dans un navigateur.
Est-ce encore vrai dans le cas de XML ? Ce chapitre va essayer de répondre à cette question ou du
moins de fournir des éléments d’appréciation. Nous commençons par partir du postulat que la majeure
partie des informations restent à l’heure actuelle stockées dans des bases de données, et qu’elles doivent en
être extraites au format XML soit pour publier tout ou partie de la base, soit pour intégrer des fragments à
d’autres types de contenus.
La première question posée est alors d’engendrer une représentation XML d’une base de données
relationnelle. La seconde est relative aux architectures logicielles qui permettent de combiner des com-
posants aussi divers qu’une base de données, un module de conversion vers XML, un processeur XSLT
et un serveur de publication web. Nous décrivons une (petite) classe Java effectuant des conversions et
montrons comme l’intégrer dans plusieurs architectures : programme batch, servlet, et JSP. Nous prenons
ensuite deux exemples très différents d’environnements qui fournissent des outils permettant la publication
de base de données :
203
204 CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
1. Cocoon, un environnement de publication complet incluant XSP, une adaptation des JSP pour pro-
duire dynamiquement du XML ;
2. XSQL, un outil Oracle qui traduit automatiquement le résultat d’une requête SQL en XML, et permet
l’intégration avec XSLT.
Pour conclure le chapitre nous reprenons la question de fond posée ci-dessus : a-t-on besoin d’utiliser
une base de données en association avec XML ? Ne pourrait-on pas utiliser XML comme format de base
de données et effectuer directement l’interrogation sur des données XML, évitant ainsi des conversions
parfois coûteuses ? Nous reprenons les principales fonctionnalités d’un système de gestion de base de
données, discutons des limitations d’une représentation XML dans un tel cadre et présentons brièvement
des techniques émergentes (schémas, langages d’interrogation) qui visent à palier ces limites.
On trouve donc quatre tables, chacune ayant un nom distinct. Elles correspondent respectivement à la
description des stations, des activités proposées dans chaque station, des clients et des séjours effectués par
les clients dans les stations. Pour chaque table on a donné la liste des attributs, sans entrer pour l’instant
dans le détail des types de données. Parmi ces attributs certains présentent une importance particulière.
La clé primaire est le (ou les) attribut(s) dont la valeur va permettre d’identifier de manière unique une
ligne dans la table. Les clés primaires sont représentées en gras. Pour le schéma ci-dessus, cela signifie
qu’il ne peut pas y avoir deux stations avec le même nom. Le choix des clés relève de choix de conception
(souvent assez délicats) que nous ne commenterons pas ici. Pour la table Client, on a visiblement considéré
que le nom n’était pas suffisant pour identifier une ligne de la table, et on a créé un attribut artificiel id
pour numéroter les clients au fur et à mesure de leur insertion.
Certaines clés peuvent être constituées de plusieurs attributs. Chaque ligne de la table Activité par
exemple est identifiée par le nom de la station, et le libellé de l’activité. Il peut donc y avoir plusieurs
activités de libellés différents dans une même station, et plusieurs activités de mêmes libellés dans des
stations différentes. Enfin la table Séjour est identifiée par trois attributs : un client peut revenir plusieurs
fois dans la même station, mais pas à la même date.
7.1. BASES DE DONNÉES ET XML 205
Une clé étrangère est un (ou plusieurs) attribut(s) dans une table l dont la valeur fait référence à une
valeur de clé primaire dans une table m . Prenons l’exemple des tables de la figure 7.1. L’attribut idClient
dans la table Séjour contient une valeur de clé de la table Client. De même l’attribut station contient une
valeur de clé de la table Station. Les clés étrangères constituent des références entre tables, avec deux
objectifs :
g définir des contraintes d’intégrité référentielle : on ne devrait pas par exemple pouvoir référencer
dans la table Séjour un client qui n’existe pas dans la table Client ;
g permettre, par des opérations dites de jointure, le regroupement de données réparties dans plusieurs
tables : à partir de la première ligne de la table Séjour, on sait par exemple que le client s’appelle
Phileas Fogg en prenant idClient et en recherchant la ligne correspondante dans la table Client.
Les clés primaire et étrangère sont les caractéristiques essentielles d’un schéma, et définissent les prin-
cipales contraintes liant les tables. D’autres contraintes, internes à chaque table, peuvent être exprimées.
Prenons par exemple la figure 7.2. On a :
1. des contraintes de types : certains attributs sont numériques, d’autres alphanumériques, avec ou sans
décimale, etc ;
2. des contraintes d’existence : la capacité de la station Santalba, ou le tarif de la plongée à Venusa,
sont inconnus, mais tous les autres attributs ont une valeur ;
3. des contraintes d’appartenance à un ensemble énuméré : on pourrait par exemple définir une codifi-
cation des régions (« Antilles », « Europe », « Amérique », « Asie », etc).
Toutes ces contraintes sont exprimables avec le langage SQL (ou plus exactement la partie de la norme
SQL ANSI relative à la définition de données). Ainsi on peut déclarer que certains attributs doivent toujours
être connus (contrainte NOT NULL), qu’il sont des instances de types SQL, ou que leur valeur appartient
à une liste fixée.
Le script ci-dessous contient les commandes de création du schéma de la base. Notez l’absence ou non
de la contrainte NOT NULL selon qu’un attribut doit être connu ou non, et la combinaison des PRIMARY
KEY et FOREIGN KEY pour décrire les liens entre les tables. Ce script est conforme à la norme, et devrait
être accepté par tout SGBD relationnel digne de ce nom (nous l’avons testé avec MySQL et ORACLE).
#
# Schéma de la base ’Agence de voyages"
#
Parlons maintenant du contenu d’une table. Il peut être vu comme un tableau constitué de colonnes,
une pour chaque attribut de la table, et de lignes, une pour chaque entité stockée dans la table. Dans le
modèle relationnel, l’ordre des lignes ou des colonnes n’est pas significatif. En pratique, cela signifie que
la requête suivante :
SELECT nomStation, lieu, région
FROM Station
WHERE capacité > 100
donnera le même résultat quel que soit le réagencement des lignes et des colonnes de Station.
En résumé, voici les aspects du modèle relationnel qui nous importent pour la conversion des données
vers XML :
<Stations>
<Station>
<nomStation>Venusa</nomStation>
<capacite>350</capacite>
<lieu>Guadeloupe</lieu>
<region>Antilles</region>
<tarif>1200.00</tarif>
</Station>
<Station>
208 CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
<nomStation>Farniente</nomStation>
<capacite>200</capacite>
<lieu>Seychelles</lieu>
<region>Océan Indien</region>
<tarif>1500.00</tarif>
</Station>
<Station>
<nomStation>Santalba</nomStation>
<capacite>null</capacite>
<lieu>Martinique</lieu>
<region>Antilles</region>
<tarif>1200.00</tarif>
</Station>
<Station>
<nomStation>Passac</nomStation>
<capacite>400</capacite>
<lieu>Alpes</lieu>
<region>Europe</region>
<tarif>1000.00</tarif>
</Station>
</Stations>
La structure obtenue est extrêmement régulière : l’arbre XML est équilibré (exactement trois niveaux
pour toutes les branches de l’arbre). De plus le contenu de chaque élément peut être précisément typé.
Il existe d’autres différences plus subtiles que celles liées à la forme entre la représentation XML et la
représentation relationnelle. La principale est que l’ordre des lignes n’est pas significatif pour une table,
alors que l’ordre des éléments l’est pour un document XML. En pratique, si toute requête SQL donne le
même résultat quel que soit l’ordre des lignes, le résultat d’un programme appliqué au document XML (par
exemple une transformation XSLT) peut dépendre de l’ordre des éléments.
Un système de gestion de base de données est libre de réorganiser le stockage des lignes d’une table, par
exemple pour optimiser les accès. Cela signifie que rien ne peut garantir que deux exportations successives
vont présenter les données dans le même ordre. Il est important de garder en tête cette particularité quand
on manipule des données XML issues d’une base de données.
Une autre différence est que la description des données (le schéma) est intégrée aux données en XML,
alors que le schéma est représenté une seule fois, et séparé des données dans un SGBD. Le document XML
est beaucoup plus redondant (et donc volumineux) que le stockage relationnel. De plus la description des
données XML est moins riche et précise que celle d’un schéma relationnel. En particulier il n’y a pas de
notion de type, et peu de contraintes sur le contenu. Nous verrons un peu plus loin comment engendrer une
DTD qui traduit (en partie) les spécifications du schéma relationnel.
Un document orienté « texte » est beaucoup moins structuré, et contient en général beaucoup de de-
scriptifs de longueurs très variables. Voici par exemple un document représentant l’avant-propos de ce
livre. Il s’agit d’un flot de texte dans lequel les balises marquent les expressions importantes, les énuméra-
tions, etc. La notion de type n’est pas importante. En revanche l’ordre est essentiel, et on ne peut pas le
changer sans changer la signification du document.
On ne peut pas considérer qu’il existe une séparation stricte entre les documents orientés « texte » et les
documents orientés « données ». Dans de nombreux cas le contenu d’un système d’information sera mixte,
et rassemblera des informations de nature et de provenance différentes. Voici un document présentant une
promotion pour notre agence de voyage. On y trouve un descriptif libre placé sous la responsabilité de
l’initiateur de la promotion, et des informations issues de la base de données.
<Promotion auteur="Jules">
<Description>
Nous proposons une réduction de <Reduction>25</Reduction>
pourcent, restreinte à la période du
<Periode> <Debut>Septembre 2001</Debut>
au <Fin>Octobre 2001</Fin> </Periode> pour tous
les séjours dans certaines stations de vacances.
L’automne est une saison <Important>merveilleuse</Important>
pour reprendre des forces à la montagne. Vous pourrez
profiter du calme,
d’une nature aux couleurs chatoyantes, et d’un contact
privilégié avec l’autochtone. <Important>Attention
le nombde de places offertes est limité.</Important>
</Description>
<Station>
<nomStation>Passac</nomStation>
<capacite>400</capacite>
<lieu>Alpes</lieu>
<region>Europe</region>
<tarif>1000.00</tarif>
<Activite>
<libelle>Ski</libelle>
<prix>200.00</prix>
</Activite>
<Activite>
<libelle>Piscine</libelle>
<prix>20.00</prix>
</Station>
</Promotion>
Toute la question dans ce cas est de définir le système qui va permettre d’intégrer le plus facilement
possible les deux types d’informations. On peut penser à stocker le document « texte » XML dans la base
de données, ce qui est tout à fait possible mais rend beaucoup plus difficile l’accès et la mise à jour pour un
utilisateur. À l’inverse on peut fournir des moyens simples (si possible..) d’inclure dynamiquement dans un
document XML des extraits d’une base de données. Nous montrerons dans la suite de ce chapitre un exem-
ple relativement simple mais complet d’une réalisation d’un document XML dynamique, et l’enchaînement
de la génération du document avec l’application d’un programme XSLT.
Éléments ou attributs ?
La première solution, immédiate, consiste à conserver la structure « plate » de la base relationnelle, et à
transcrire chaque table par un élément ayant le nom de la table ou un dérivé (par exemple h Stations i ).
Cet élément contient lui-même un élément pour chaque ligne, ayant pour nom un autre dérivé du nom de la
table (par exemplr « Station », sans « s », enfin chaque attribut de la table est représenté par un élément,
constituant ainsi un troisième niveau dans l’arbre.
Il existe (au moins) une autre possibilité. Au lieu de représenter les attributs de la table par des éléments,
on peut les représenter par des attributs XML de l’élément représentant la ligne. Voici ce que cela donnerait
pour la table Station.
210 CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
<Stations>
<Station nomStation=’Venusa’
capacite=’350’
lieu=’Guadeloupe’
region=’Antilles’
tarif=’1200.00’
/>
<Station nomStation=’Farniente’
capacite=’200’
lieu=’Seychelles’
region=’Océan Indien’
tarif=’1500.00’
/>
<Station nomStation=’Santalba’
capacite=’null’
lieu=’Martinique’
region=’Antilles’
tarif=’1200.00’
/>
<Station nomStation=’Passac’
capacite=’400’
lieu=’Alpes’
region=’Europe’
tarif=’1000.00’
/>
</Stations>
Cette méthode présente quelques avantages. Tout d’abord elle est assez proche, conceptuellement, de
la représentation relationnelle. Chaque ligne d’une table devient un élément XML, chaque attribut de la
ligne devient un attribut XML de l’élément. La structure est plus fidèlement retranscrite, et notamment le
fait qu’une ligne d’une table forme un tout, manipulé solidairement par les langages. En SQL par exemple,
on n’accède jamais à un attribut sans être d’abord passé par la ligne de la table.
Techniquement, l’absence d’ordre (significatif) sur les attributs XML correspond à l’absence d’ordre
significatif sur les colonnes d’une table. On s’affranchit donc en partie des problèmes potentiels évoqués
précédemment. Du point de vue du typage, l’utilisation des attributs permet également d’être plus précis
et plus proche de la représentation relationnelle :
g on ne peut pas avoir deux fois le même attribut pour un élément, de même qu’on ne peut pas avoir
deux colonnes avec le même nom dans une table (notez que ce n’est pas vrai si on représente les
colonnes par des éléments XML) ;
g on peut indiquer, dans un DTD, la liste des valeurs que prend un attribut, ce qui renforce un peu les
contrôles sur le document.
Enfin l’utilisation des attributs aboutit à un document moins volumineux. Nous utiliserons donc plutôt
cette méthode de conversion par la suite, bien qu’il soit juste de souligner que la représentation des colonnes
par des éléments a aussi ses défenseurs. Comme pour beaucoup d’autres problèmes sans solution tranchée,
le choix dépend en fait beaucoup de l’application et de l’utilisation qui est faite des informations.
dans une autre table. L’opération (relationnelle) qui va ensuite s’appuyer sur cette correspondance pour
rapprocher les lignes de deux tables est la jointure. Dans la figure 7.2, page 206, on voit par exemple
que les tables Station et Activité sont indépendantes l’une de l’autre (elles peuvent être stockées en des
endroits différents), mais que l’on peut, par calcul, reconstituer l’association en prenant, pour chaque ligne
décrivant une station, les lignes de Activité ayant le nom de la station.
En d’autres termes, la condition nécessaire et suffisante pour qu’il soit possible de reconstituer l’information
est l’existence d’un critère de rapprochement. Il est tout à fait possible d’appliquer le même principe en
XML. Voici par exemple un document où figurent des éléments de type Station et de type Activite
(nous n’avons gardé que deux stations pour simplifier). Ces éléments sont indépendants les uns des autres
(ici cela signifie que des informations apparentées ne sont pas liées dans la structure hiérarchique du docu-
ment), mais on a conservé le critère de rapprochement.
<Stations>
<Station nomStation=’Venusa’
capacite=’350’
lieu=’Guadeloupe’
region=’Antilles’
tarif=’1200.00’
/>
<Station nomStation=’Farniente’
capacite=’200’
lieu=’Seychelles’
region=’Océan Indien’
tarif=’1500.00’
/>
<Activite nomStation=’Venusa’
libelle=’Voile’
prix=’150.00’
/>
<Activite nomStation=’Venusa’
libelle=’Plongee’
/>
<Activite nomStation=’Farniente’
libelle=’Plongée’
prix=’130.00’
/>
</Stations>
Maintenant, comme dans le cas du relationnel, il est possible de déterminer par calcul, la correspon-
dance entre une activité et une station. Supposons que l’on exécute un programme XSLT. Si le nœud
courant est de type Station, il est possible de sélectionner toutes les activités avec l’expression XPath :
/Activite[@nomStation=current()/@nomStation]
De même, si le nœud courant est de type Activite, voici l’expression XPath qui désigne la station :
/Station[@nomStation=current()/@nomStation]
212 CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
Cette recherche s’apparente à la jointure de SQL, même si on procède assez différemment ici : il faut
d’abord se positionner sur un nœud, puis exprimer avec XPath le chemin qui mène aux nœuds associés.
Cette représentation est possible, mais elle n’est pas naturelle en XML et mène à quelques difficultés.
Elle n’est pas naturelle parce qu’une activité est toujours dans une et une seule station : il est donc inutile de
la représenter séparément. Elle présente des difficultés parce qu’il va être beaucoup coûteux de parcourir
des chemins compliqués dans l’arbre pour relier des nœuds.
La bonne représentation dans ce cas consiste à imbriquer les éléments de type Activite dans l’élément
de type Station. On peut du même coup s’épargner la peine de conserver le nom de la station dans
les éléments h Activite i puisque la correspondance Station/Activité est maintenant représentée par la
structure, et pas par un lien de navigation basé sur des valeurs communes. Voici ce que cela donne.
<Stations>
<Station nomStation=’Venusa’
capacite=’350’
lieu=’Guadeloupe’
region=’Antilles’
tarif=’1200.00’>
<Station nomStation=’Farniente’
capacite=’200’
lieu=’Seychelles’
region=’Océan Indien’
tarif=’1500.00’>
</Stations>
Cette représentation est bien meilleure. Il est maintenant possible, pour un nœud courant de type
Station, d’accéder à toutes les activités avec l’expression XPath Activite. Inversement l’expression
« .. » donne la station pour une activité. Ces expressions s’évaluent très efficacement puisque les infor-
mations apparentées sont localisées dans la même partie de l’arbre XML. Notons au passage qu’il n’est
pas possible d’utiliser d’imbrication dans le relationnel classique (les SGBD « relationnel/objet » récents
savent le faire). Tout y est représenté « à plat », ce qui mène à multiplier le nombre des tables, souvent
inutilement.
La méthode ci-dessus ne pose pas de problème dans le cas où l’un des éléments (ici h Activite i )
est lié à un et un seul autre (ici h Station i ). On parle d’association de un à plusieurs en modélisation
base de données.
En revanche, dans le cas d’associations « plusieurs à plusieurs », l’imbrication ne va pas de soi. Prenons
par exemple l’association entre les stations et les clients. Pour une station il peut y avoir plusieurs clients,
et réciproquement. Dans le schéma relationnel on a créé une table intermédiaire Séjour pour représenter
cette association.
Il n’est pas évident de choisir l’ordre d’imbrication des éléments. Tout dépend de l’ordre de navigation
qui va être employé. Si on suppose par exemple que les accès se feront par les stations, on peut choisir
l’imbrication représentée dans l’exemple suivant (toujours en nous limitant à deux stations) :
Exemple 91 StationSejour.xml : Les stations et leurs séjours imbriqués, avec les clients à part
7.1. BASES DE DONNÉES ET XML 213
<Stations>
<Station nomStation=’Venusa’
capacite=’350’
lieu=’Guadeloupe’
region=’Antilles’
tarif=’1200.00’>
<Sejour idClient=’30’
debut=’2001-08-03’
nbPlaces=’3’/>
<Sejour idClient=’20’
debut=’2001-08-03’
nbPlaces=’6’/>
</Station>
<Station nomStation=’Farniente’
capacite=’200’
lieu=’Seychelles’
region=’Océan Indien’
tarif=’1500.00’>
<Sejour idClient=’30’
debut=’2002-06-24’
nbPlaces=’5’/>
<Sejour idClient=’10’
debut=’2002-09-05’
nbPlaces=’3’/>
</Station>
<Client id=’10’
nom=’Fogg’
prenom=’Phileas’
ville=’Londres’
region=’Europe’
solde=’12465.00’
/>
<Client id=’20’
nom=’Pascal’
prenom=’Blaise’
ville=’Paris’
region=’Europe’
solde=’6763.00’
/>
<Client id=’30’
nom=’Kerouac’
prenom=’Jack’
ville=’New York’
region=’Amérique’
solde=’9812.00’
/>
</Stations>
Un séjour est lié à une seule station, donc on a appliqué la méthode d’imbrication. Il est très facile,
214 CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
si le nœud courant est de type Station, d’accéder aux séjours de la station avec l’expression XPath
textttSejour. Le problème se pose maintenant pour les clients qui ont été laissés à part. Si, étant sur un
nœud client, on recherche tous les séjours d’un client, l’expression correspondante est :
/Station/Sejour[@idClient=current()/id]
En introduisant une hiérarchie Station/Séjour, on a donc privilégié un chemin d’accès aux données. Dès
que, travaillant sur l’information relatives aux stations et à leurs séjours, on voudra obtenir celle relative
aux clients, il faudra suivre des liens de navigation et accéder à une partie éloignée de l’arbre, ce qui est
difficile à exprimer, et difficile à évaluer pour le processeur.
On peut compléter la hiérarchie en y introduisant les clients, ce qui permet de supprimer le lien de
navigation basé sur l’identifiant du client. On obtient alors le document suivant.
<Stations>
<Station nomStation=’Venusa’
capacite=’350’
lieu=’Guadeloupe’
region=’Antilles’
tarif=’1200.00’>
<Sejour debut=’2001-08-03’
nbPlaces=’3’>
<Client id=’30’ nom=’Kerouac’ prenom=’Jack’
ville=’New York’ region=’Amérique’ solde=’9812.00’/>
</Sejour>
<Sejour debut=’2001-08-03’
nbPlaces=’6’>
<Client id=’20’ nom=’Pascal’ prenom=’Blaise’
ville=’Paris’ region=’Europe’ solde=’6763.00’/>
</Sejour>
</Station>
<Station nomStation=’Farniente’
capacite=’200’
lieu=’Seychelles’
region=’Océan Indien’
tarif=’1500.00’>
<Sejour debut=’2002-06-24’
nbPlaces=’5’>
<Client id=’30’ nom=’Kerouac’ prenom=’Jack’
ville=’New York’ region=’Amérique’ solde=’9812.00’/>
</Sejour>
<Sejour debut=’2002-09-05’
nbPlaces=’3’>
<Client id=’10’ nom=’Fogg’ prenom=’Phileas’
ville=’Londres’ region=’Europe’ solde=’12465.00’/>
</Sejour>
</Station>
</Stations>
7.1. BASES DE DONNÉES ET XML 215
Cette fois, en supposant que le point d’accès soit toujours une station, on a toutes les informations
situées dans le même sous-arbre, ce qui va permettre d’y accéder efficacement et simplement. On voit en
revanche que si on souhaite prendre comme point d’accès un client, les informations utiles sont réparties
un peu partout dans l’arbre, et que leur reconstitution sera plus difficile.
Une autre conséquence de la structure hiérarchique propre aux documents XML est qu’il est difficile
d’éviter la redondance d’information. Le client Jack Kerouac, avec toutes ses caractéristiques, apparaît
par exemple plusieurs fois dans le document StationSejourClient.xml. Si ce document ne sert que pour une
publication de données, ou plus généralement pour des recherches d’information, alors le seul inconvénient
est la taille plus importante du document par rapport à celui obtenu en mettant un seul exemplaire de chaque
élément, et en les associant par des liens.
La base de données que nous utilisons dans nos exemple est très simple. Il est clair que pour des bases
réalistes présentant quelques dizaines de tables, la conception d’un schéma XML d’exportation doit faire
des compromis entre l’imbrication des données et la conservation des correspondances clé primaire/clé
étrangère sous forme de lien de navigation dans le document XML. Tout dépend alors des besoins de
l’application, de la partie de la base qu’il faut exporter, et des chemins d’accès privilégiés aux informations
qui seront utilisés dans l’exploitation du document.
Racine du document
Les documents auront un élément racine de type Stations, constitué de 0 ou plusieurs éléments de type
Station.
Chaque colonne de la table devient un attribut de l’élément. Nous ne pouvons pas reproduire le type,
donc tous sont déclarés comme CDATA. En ce qui concerne la contrainte d’existence NOT NULL, elle a
une équivalence directe dans la DTD.
1. si la colonne est à NOT NULL, l’attribut XML est qualifié avec #REQUIRED ;
2. sinon l’attribut XML est qualifié avec #IMPLIED.
On choisit donc de ne pas représenter un attribut à NULL, ce qui est le plus proche équivalent en XML
du fait que la valeur est inconnue. La valeur NULL correspond vraiment à une absence de valeur, ce qui est
très différent d’une valeur à 0 ou de la chaîne vide, cette dernière pouvant être représentée par un élément
ou un attribut vide en XML.
Les autres contraintes sur le contenu de la table sont d’une part la clé primaire, d’autre part l’ensemble
de valeurs énumérées pour la colonne region. Quand la clé primaire est constituée d’une seule colonne,
elle correspond à l’ID d’une DTD XML :
nomStation ID #REQUIRED
Si le parseur est validant (autrement dit s’il vérifie que le document est conforme à la DTD), il contrôlera
qu’il n’y a pas deux éléments h Station i avec le même attribut nomStation. En fait la portée d’une
déclaration d’identifiant couvre tous les éléments d’un document, quel que soit leur type, alors qu’elle est
restreinte à une table en relationnel. Si on déclare un ID pour un autre élément correspondant à une autre
table, il faut envisager qu’il puisse exister des valeurs de clé identiques dans les deux tables (dans ce cas on
peut par exemple préfixer les clés par le nom de la table, même si ce n’est pas une garantie totale). Dans
le cas – fréquent – où la clé primaire comprend plusieurs colonnes de la table, il n’existe pas d’équivalent
dans la notation des DTD.
Pour la valeur énumérée, on peut aussi l’indiquer dans la DTD. Notez que, par chance (!), il n’y a pas
de valeur contenant des blancs dans notre liste des régions, ce qui serait possible dans le schéma relationnel
mais ne pourrait pas être retranscrit dans la DTD.
Finalement il reste à indiquer qu’un élément h Station i peut avoir comme fils 0 ou plusieurs élé-
ments h Sejour i . Voici la partie de la DTD correspondant à la table Station.
Cette table comprend deux clé étrangères : chaque séjour fait référence à une (une seule) station et
un (un seul) client. Comme nous avons choisi de représenter par une imbrication les liens entre ces trois
entités, la reprise de ces clés étrangères dans la DTD est inutile. Il sufit de dire qu’un élément de type
Sejour est composé d’un (un seul) élément de type Client, un élément h Sejour i étant par ailleurs
fils d’un et un seul élément h Station i .
Enfin la table Client est retranscrite en reprenant simplement les colonnes de la tables comme attributs
dans la DTD. Notez que l’on ne peut plus dire que l’attribut id est unique dans le document puisqu’un
client sera dupliqué pour chaque séjour auquel il a participé (voir document StationSejourClient.xml,
page 214). On doit quand même conserver l’attribut id dans l’élément, pour pouvoir reconnaître les
éléments correspondant au même client.
La table Activite
Pour les besoins de la cause, nous plaçons les éléments de type Activite indépendamment de la station
à laquelles ils se rattachent, en conservant un lien identique à celui de la base relationnelle. Voici la
commande de création de cette table.
On ne peut pas déclarer d’identifiant puisqu’il est composé de deux colonnes. En revanche il est possi-
ble de transcrire partiellement la clé étrangère par une référence IDREF.
<!ELEMENT Activite EMPTY>
<!ATTLIST Activite
nomStation IDREF #REQUIRED
libelle CDATA #REQUIRED
prix CDATA #IMPLIED
>
Un processeur validant vérifiera, sur notre exemple, que chaque attribut nomStation d’un élément
h Activite i a une valeur égale à celle de l’attribut correspondant dans un élément h Station i . Con-
trairement au schéma de la table relationnelle, rien n’indique que nomStation dans h Activite i fait
référence à nomStation dans h Station i puisque les identifiants sont globaux au document.
Voici pour conclure la DTD complète.
7.2 Architectures
Nous allons maintenant étudier quelques architectures-types permettant d’intégrer dans un système de pub-
lication des informations extraites d’une base de données. Il n’existe pas à l’heure actuelle de mécanisme
standard, mais on peut dégager quelques principes généraux sur lesquels s’appuient la plupart des outils.
Une des principales motivations dans ce type d’architecture est la séparation des points de vues (sep-
aration of concern). L’idée est que dans un système basé sur plusieurs couches logicielles présentant
chacune un haut degré de complexité, le passage d’une couche à une autre doit être le plus transparent
possible. Concrètement, les développeurs XSLT ne devraient pas avoir à connaître aussi un langage de
programmation come Java, la structure de la base de données ou l’interface JDBC.
Il existe de nombreux outils qui exportent en XML une partie d’une base de données, et permettent
l’intégration avec XSLT. Dans la mesure où ces outils diffèrent les uns des autres et sont amenés, de plus,
à évoluer rapidement, nous avons choisi de présenter tout d’abord un exemple complet et autonome de
réalisation d’un extracteur et d’insertion de cet extracteur dans divers types d’architectures. Cet exemple
est bien sûr simplifié mais doit permettre de comprendre rapidement les problèmes posés par la création de
XML « dynamique ».
Nous décrirons ensuite deux outils assez différents qui proposent, chacun à sa manière, une solution à
ces problèmes : l’environnement de publication Cocoon, et les outils XML d’Oracle.
3. mise en forme du résultat de la requête en représentant chaque ligne soit par des éléments XML, soit
par des attributs.
Bien entendu cette classe, ExportXML.java, utilise JDBC, l’interface standard de connexion aux bases
de données relationnelles. Voici le code de la classe, les méthodes formatXML(), formatEle-
ments() et formatAttributs() étant décrites par la suite.
// Connection à la base
connexion = DriverManager.getConnection
(nomBase, login, password);
}
Le constructeur de la classe (appelé au moment de l’instanciation d’un nouvel objet avec new) reçoit
le nom du driver JDBC à utiliser, et les paramètres de connexion à la base. Le constructeur cherche alors
à instancier le driver (ce qui suppose que la classe soit accessible dans le CLASSPATH au moment de
l’exécution), et essaie de se connecter à la base avec les paramètres fournis. Si l’une de ces opérations
échoue, une exception Java est envoyée au programme appelant.
220 CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
Remarque : Nous considérons comme acquise une connaissance de base de JDBC, suffisante pour com-
prendre nos exemples. Pour en savoir plus, reportez-vous à un ouvrage spécialisé, par exemple JDBC et
Java, Guide du programmeur, par George Reese, aux Éditions O’Reilly.
Si l’instanciation d’un objet de la classe ExportXML réussit, l’objet est connecté à la base et prêt à
exécuter et formatter des requêtes. Ce service est fourni par la méthode formatXML() ci-dessous.
public String formatXML (String requete,
String nomElement,
String mode)
{
StringBuffer chaineXML = new StringBuffer ();
Statement reqJDBC; // Le ’statement’ JDBC
ResultSet resultat; // Le résultat de la requête
ResultSetMetaData schema; // Le schéma de la requête
// Exécution de la requête
try
{
reqJDBC = connexion.createStatement ();
resultat = reqJDBC.executeQuery (requete);
schema = resultat.getMetaData();
return chaineXML.toString();
}
La méthode reçoit trois arguments : la requête à exécuter, le nom de l’élément décrivant chaque ligne,
et le mode de formattage qui peut être elements ou attributs. La requête est alors exécutée, et on
récupère deux variables : resultat est un curseur sur la liste des lignes renvoyées par l’exécution de la
requête, et schema est la description du résultat (nombre d’attributs, nom des attributs, etc). Il ne reste
plus qu’à appeler la méthode qui, selon le cas, va mettre en forme chaque ligne avec des éléments ou des
attributs. Voici par exemple formatAttributs(), formatElements() étant tout à fait semblable
(vous pouvez bien entendu récupérer le code complet sur le site).
private String formatAttributs (String nomElement,
ResultSet resultat,
7.2. ARCHITECTURES 221
ResultSetMetaData schema)
throws SQLException
{
StringBuffer chaine = new StringBuffer();
String valeur, nom ;
int i, nbAttrs;
La méthode crée un élément (le nom est celui passé en paramètre) et insère autant d’attributs XML
qu’il y a de colonnes dans le résultat. Noter que les valeurs nullles ne sont pas prises en compte grâce au
test :
if (!resultat.wasNull())
Pour compléter ce code, il faudrait tester que chaque valeur issue de la base ne contient pas de caractère
réservé XML comme « < », « > » ou « & », et les remplacer éventuellement par des entités. Telle quelle,
cette classe est déjà suffisante pour étudier les différents types d’architecture dans lesquels elle peut prendre
place.
Avant d’étudier des solutions basées sur des servlet, JSP ou XSP, mentionnons le cas le plus simple,
celui d’un programme lancé sur la ligne de commande qui prend en argument la requête, et sort le document
XML.
Le programme Extracteur.java ci-dessous se connecte à une base « agence de voyage » gérée par
MySQL. Il prend en paramètre, sur la ligne de commande, n’importe quelle requête sur cette base, ainsi
que le nom de l’élément représentant chaque ligne du résultat, et le nom de l’élément racine du document.
Tout l’export est pris en charge par une instance de Export.java.
import java.util.*;
import ExportXML;
class Extracteur
{
public static void main (String args [])
{
try
{
ExportXML export = new ExportXML
("org.gjt.mm.mysql.Driver",
"jdbc:mysql://localhost/Station",
"visiteurStation", "mdpVisiteur");
System.out.println
("<?xml version=’1.0’ encoding=’ISO-8859-1?>’\n");
L’utilisation d’un programme comme celui-ci, combiné à des transformations XSLT pour créer par ex-
emple un site HTML, constitue une solution simple et robuste qui ne pose pas de problème de performance
ou de disponibilité du serveur. Le seul inconvénient, qui doit être apprécié en fonction des caractéristiques
de l’application, est l’absence de rafraîchissement en temps réel des données. Nous passons maintenant à
des solutions plus sophistiquées où le représentation XML est produite dynamiquement.
BD
Machine serveur
sur le serveur, appliquer un programme XSLT afin d’obtenir une version HTML transmise au client. Le
résultat « brut » fourni par la servlet est donné dans la figure 7.5.
Voyons maintenant comment on met en œuvre ce service web. Le code complet de la servlet est donné
ci-dessous.
import ExportXML;
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
try
{
// Exécution de la requête
export = new ExportXML ("org.gjt.mm.mysql.Driver",
"jdbc:mysql://localhost/Station",
"visiteurStation", "mdpVisiteur");
// Extraction XML
chaineXML.append (export.formatXML (requete,
element,
224 CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
mode));
// Affichage du résultat
out.println("<?xml version=’1.0’ encoding=’ISO-8859-1?>’\n");
out.println("<" + racine + ">");
out.println (chaineXML.toString());
out.println("</" + racine + ">");
}
catch (Exception e)
{
chaineXML.append ("Erreur JDBC: " + e.getMessage());
}
}
}
La servlet ServletXML est une sous-classe HttpServlet, et hérite donc de toutes ses fonctionnal-
ités. En particulier elle reçoit deux paramètres, request et response qui représentent respectivement
un objet contenant toutes les caractéristiques de la requête HTPP transmise à la servlet, et un objet servant
d’intermédiaire pour construire et communiquer le résultat. On se sert de cet objet pour indiquer que le
type MIME du document fourni est text/plain (on peut également utiliser text/xml si le navigateur
le connaît) au lieu de l’habituel text/html.
response.setContentType("text/plain");
La première tâche consiste à récupérer les paramètres requete, racine, element et mode provenant
du formulaire. Ensuite on instancie un objet de la classe ExportXML et on le charge d’exécuter la requête
et de constituer une représentation XML appropriée du résultat. Enfin on place ce résultat dans le document
résultat transmis par le serveur au navigateur (ou à tout autre programme client ayant effectué la requête
HTTP).
7.2. ARCHITECTURES 225
Cette servlet très simple fournit donc un moyen de publier sous forme XML dees extraits de la base de
données. Sur ce même canevas on peut construire des services plus ou moins complexes qui vont permettre
d’échanger des documents XML construits dynamiquement avec d’autres applications (voir chapitre 6).
Maintenant la question qui peut nous préoccuper est : où faire intervenir une transformation XLT ? Il existe
(au moins..) deux possibilités.
1. Laisser le programme client traiter le document à sa guise. C’est la situation où on publie sur le
web des documents qui, contrairement aux pages HTML, sont exploitables : on peut en extraire des
informations, les restructurer, les intégrer à d’autres, etc. Nous avons déjà discuté dans les chapitres
précédents de ce livre du rôle de XML/XSLT dans l’intégration de données. Dans ces circonstances
XSLT est utilisé du côté client pour traduire le document fourni dans sa propre DTD (exemple d’un
moteur de recherche).
2. Effectuer la transformation côté serveur. Dans ce cas on connaît un service sur le web qui « consomme »
des documents conformes à une certaine DTD, et c’est au niveau du serveur que la transformation
est effectuée afin de communiquer des informations au bon format à ce service.
Pour effectuer la transformation côté serveur, il faut, avant la transmission du document XML au client,
lui appliquer un programme XSLT. On peut le faire dans notre servlet en appelant les services Java d’un
processeur XSLT (par exemple Xalan). Nous allons montrer une solution un peu plus simple à base de
Java Server Pages.
ligne, à la fonction println(), ce qui alourdit considérablement le code et va à l’encontre du partage des
tâches entre les différents intervenants d’un système de publication.
Dans une servlet, tout est programmé. Il n’ya donc plus de distinction entre le contenu, la présentation
et les parties « applicatives » (accès à la base, calculs, etc). Au contraire, avec les JSP, on peut constituer
un service web de plusieurs couches (figure 7.6) :
La figure 7.6 montre cette architecture, la couche applicative étant représentée par un composant Java
Bean.
Serveur JDBC
JSP JavaBean
web
Représentation XML Objet intermédiaire
BD
Au départ les JSP sont principalement destinées à faciliter la production de HTML dynamiquement. On
peut donc les considérer, au même titre que les ASP ou que PHP, comme un moyen d’introduire souplement
des parties programmées dans des pages HTML. Voici un exemple très simple de page JSP, illustrant ce
mécanisme.
Exemple 96 JSPSimple.jsp : Un exemple simple de page JSP
<HTML>
<HEAD><TITLE>JSP Simple</TITLE></HEAD>
<BODY>
</BODY>
</HTML>
Il s’agit donc d’un document HTML, au sein duquel les parties programmées en Java sont placées dans
des balises marquées par <%. Au moment où le serveur lit ces pages, il va compiler les parties programmées
et les associer aux parties statiques dans une servlet transitoire qui est alors exécutée. Les JSP peuvent très
bien être considérées comme un moyen de faciliter l’écriture de servlets.
Les JSP étant initialement orientées vers HTML, les notions de contenu et de présentation ne sont pas
encore clairement séparées. On peut cependant utiliser les JSP pour produire du XML, et tirer parti de
la capacité des JSP à « masquer » les parties programmées. Nous allons illustrer les JSP en prenant pour
exemple un petit formulaire qui permet de donner le nom d’une station, et d’obtenir en retour un document
XML représentant cette station. La requête est donc toujours :
7.2. ARCHITECTURES 227
public XMLBean()
{
try
{
export = new ExportXML ("org.gjt.mm.mysql.Driver",
"jdbc:mysql://localhost/Station",
"visiteurStation", "mdpVisiteur");
}
catch (Exception e)
{
chaineXML.append ("Impossible de se connecter à MySQL : ");
chaineXML.append (e.getMessage());
}
mode = "attributs";
nomStation = "";
element = "Station";
}
Cette classe est très simple. Son principal intérêt est de se conformer à une interface normalisé qui va
permettre de faire appel à ses services très facilement dans une page JSP. Chaque propriété (nomStation,
element, mode) est modifiée par une méthode nommée setXXX() où XXX est le nom de la propriété.
Réciproquement des méthodes getXXX() permettent de récupérer la valeur d’une propriété. Dans le
code précédent nous n’avons mis que les méthodes dont nous avons vraiment besoin pour simplifier la
présentation.
Voici maintenant la page JSP. On peut constater que cette fois le code Java est complètement caché.
On utilise, de manière transparente, les fonctionnalités du bean avec des instructions JSP :
1. l’instruction n jsp:useBean id="monBean" class="XMLBean"/ o est l’équivalent, en JSP,
de l’instanciation d’un objet : on lui donne un nom qui permettra d’y faire référence, et on indique
sa classe ;
2. l’instruction n jsp:setProperty name="monBean" property="nomStation"/ o re-
vient à appeler la méthode setNomStation() sur l’objet monBean : on l’occurrence on affecte
à cette propriété du bean la valeur transmise par le formulaire ;
3. inversement, l’instruction n jsp:getProperty name="monBean" property="chaineXML"/ o
revient à appeler la méthode getChaineXML() sur l’objet monBean.
La modification ou l’extraction de certaines propriétés déclenche des opérations internes (accès à la
base, mise en forme XML) qui sont transparentes dans cette page. On obtient donc l’équivalent de la
servlet de la section précédente, en obtenant une séparation relativement satisfaisante du contenu (ici un
document XML dynamique que l’on pourrait facilement intégrer à des parties statiques) et de la « logique »
(ici un accès à une base de donnée). Voici un exemple plus concret qui reprend le document Promotion.xml,
page 208, dont nous avions signalé qu’il comprenait des parties statiques, et des parties extraites de la base.
Nous disposons maintenant d’un moyen technique d’intégrer ces parties dynamiques.
Exemple 99 Promotion.jsp : Le document Promotion.xml avec du code JSP
<?xml version="1.0" encoding="ISO-8859-1"?>
<Promotion auteur="Jules">
<Description>
Nous proposons une réduction de <Reduction>25</Reduction
pourcent, restreinte à la période du
<Periode> <Debut>Septembre 2001</Debut>
au <Fin>Octobre 2001</Fin> </Periode> pour tous
les séjours dans certaines stations de vacances.
L’automne est une saison <Important>merveilleuse</Important>
pour reprendre des forces à la montagne. Pour pourrez
profiter du calme,
d’une nature aux couleurs chatoyantes, et d’un contact
privilégié avec l’autochtone. <Important>Attention
le nombre de places offertes est limité.</Important>
</Description>
C’est presque un document XML bien formé, à l’exception des instructions JSP pour les parties dy-
namiques.
Il reste à intégrer la présentation des données obtenues avec un programme XSLT. JSP offre la possi-
bilité de créer des librairies de balises qui permettent d’invoquer simplement des fonctionnalités comme
l’application d’un programme XSLT. Des librairies sont fournies par la fondation Apache, sur le site
http://jakarta.apache.org/taglibs. Voici un exemple de leur utilisation :
Exemple 100 PromotionXSL.jsp : Transformation du document Promotion.jsp avec un programme XSLT
<%@ taglib uri="http://jakarta.apache.org/taglibs/xsl-1.0"
prefix="xsltlib" %>
<xsltlib:apply xsl="/bdxml/Promotion.xsl">
<?xml version="1.0" encoding="ISO-8859-1"?>
<Promotion auteur="Jules">
<Description>
Nous proposons une réduction de <Reduction>25</Reduction>
pourcent, restreinte à la période du
<Periode> <Debut>Septembre 2001</Debut>
au <Fin>Octobre 2001</Fin> </Periode> pour tous
les séjours dans certaines stations de vacances.
L’automne est une saison <Important>merveilleuse</Important>
pour reprendre des forces à la montagne. Vous pourrez
profiter du calme,
d’une nature aux couleurs chatoyantes, et d’un contact
privilégié avec l’autochtone. <Important>Attention
le nombre de places offertes est limité.</Important>
</Description>
Le programme JSP commence par déclarer l’utilisation de la librairie de balises xsl-1.0, avec le
préfixe xsltlib.
<xsltlib:apply xsl="/bdxml/Promotion.xsl">
...
</xsltlib:apply>
Cette solution n’est pas encore totalement satisfaisante puisque des fragments XML sont associés à
des balises non-XML, ce qui empêche de les utiliser indépendamment du service web, par exemple pour
effectuer une transformation XSL-FO.
En résumé les JSP offrent un premier exemple d’une architecture cherchant à « séparer les points de
vue » du programmeur, de l’administrateur de bases de données, et du gestionnaire de présentation. Mal-
heureusement la séparation n’est pas encore parfaite entre l’aspect « contenu » et l’aspect « présentation ».
230 CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
Une page JSP contenant et engendrant du XML n’est pas elle-même du XML bien formé, et l’utilisation
de XML/XSLT est donc limitée à la production de fragments assemblés par des instructions JSP.
1. Cocoon est un environnement de publication open source développé dans le cadre du projet XML de
la fondation Apache. Une des innovations proposées dans le cadre de ce projet est XSP, un langage
qui généralise les JSP.
2. Oracle développe également de très nombreux outils autour de son SGBD, dont une « suite » XML.
Cette suite comprend un outil de transformation automatique du résultat d’une requête SQL en XML,
suivi d’une transformation XSLT.
Il existe de très nombreux autres produits qui s’apparentent à l’un ou l’autre : nous en donnons une liste
(pas forcément exhaustive...) sur le site associé à ce livre.
7.3.1 XSP
Les eXtensible Server Pages (XSP), proposées initialement dans le cadre du projet Cocoon, et maintenant
adoptées également par un environnement de publication comme AxKit (www.axkit.org), sont une adap-
tation des principes des JSP en mettant cette fois l’accent sur une parfaite intégration avec XML. Un
document XSP est un document XML, incluant, dans des éléments spécifiques, du code Java.
La transformation d’un document XSP s’effectue en plusieurs étapes. Tout d’abord le document XSP
est compilé et exécuté, ce qui donne un document XML intermédiaire dans lequel les instructions java
ont été remplacées par leur résultat, au format XML. Puis ce document XML intermédiaire est soumis à
nouveau à un processus de transformation, avec un programme XSLT cette fois, et le résultat produit (du
HTML, du XML, etc) est enfin transmis au programme client.
L’architecture de Cocoon1 permet une généralisation de ce processus impliquant plusieurs transforma-
tions successives d’un document. Le système est basé sur la combinaison de producteurs, chacun étant
chargé d’effectuer une transformation (par XSLT, exécution de code ou tout autre mécanisme) d’un doc-
ument XML en entrée vers un document XML en sortie (figure 7.7). En bout de chaîne un formateur est
chargé d’effectuer la mise en forme du document final (en HTML, en PDF si le document est en XSL-FO,
ou tout autre format) avant de le transmettre au client.
Présentation de XSP
Il n’est pas dans notre propos de présenter de manière appronfondie XSP. Nous allons simplement présenter
les principes de fonctionnement sur un exemple. Voici donc un document PromotionXSP.xml qui reprend
des parties statiques (tout ce qui concerne le texte de la promotion) et une partie dynamique, consistant à
interroger la base de données et à intégrer le résultat dans le document.
<?cocoon-process type="xsp"?>
<?cocoon-process type="xslt"?>
<?xml-stylesheet href="Promotion.xsl" type="text/xsl"?>
1 Nous parlons ici de la version 1.8, la version 2.0 étant encore, à l’heure où ces lignes sont écrites, en phase de développement.
7.3. XML DYNAMIQUE 231
Requête
Producteur Producteur
(autres
(XSP) transformations) (XSLT)
<xsp:page language="java"
xmlns:xsp="http://www.apache.org/1999/XSP/Core">
<xsp:structure>
<xsp:include>java.sql.*</xsp:include>
</xsp:structure>
<Promotion auteur="Jules">
<xsp:logic>
DriverManager.registerDriver(new org.gjt.mm.mysql.Driver());
// Connection à la base
Connection conn = DriverManager.getConnection
("jdbc:mysql://localhost/Station",
"visiteurStation", "mdpVisiteur");
Statement stmt = conn.createStatement ();
ResultSet resultat = stmt.executeQuery (
"select * from Station where nomStation=’Passac’");
</xsp:logic>
<Description>
Nous proposons une réduction de <Reduction>25</Reduction>
pourcent, restreinte à la période du
<Periode> <Debut>Septembre 2001</Debut>
au <Fin>Octobre 2001</Fin> </Periode> pour tous
les séjours dans certaines stations de vacances.
L’automne est une saison <Important>merveilleuse</Important>
pour reprendre des forces à la montagne. Vous pourrez
profiter du calme,
d’une nature aux couleurs chatoyantes, et d’un contact
privilégié avec l’autochtone. <Important>Attention
le nombre de places offertes est limité.</Important>
</Description>
<Station>
<nomStation><xsp:expr>resultat.getString (1)</xsp:expr></nomStation>
<capacite><xsp:expr>resultat.getString (2)</xsp:expr></capacite>
<lieu><xsp:expr>resultat.getString (3)</xsp:expr></lieu>
<region><xsp:expr>resultat.getString (4)</xsp:expr></region>
<tarif><xsp:expr>resultat.getString (5)</xsp:expr></tarif>
</Station>
232 CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
</Promotion>
</xsp:page>
Il faut bien noter pour commencer que ce document est du XML tout à fait correct. Son élément racine
est xsp:page, et il contient de nombreux éléments appartenant à l’espace de nom xsp, déclaré de la
manière suivante :
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
2. p xsp:logic q est un élément dont le contenu est constitué de code Java (attention, c’est toujours
du XML donc il ne faut pas utiliser directement « < », mais la référence à l’entité <) ;
3. p xsp:expr q enfin permet d’insérer dans le document le résultat d’une expression Java (il est
automatiquement converti en chaîne de caractères).
Dans notre exemple l’élément p xsp:logic q contient le code JDBC pour se connecter à la base et
évaluer une requête.
<xsp:logic>
DriverManager.registerDriver(new org.gjt.mm.mysql.Driver());
// Connection à la base
Connection conn = DriverManager.getConnection
("jdbc:mysql://localhost/Station",
"visiteurStation", "mdpVisiteur");
Statement stmt = conn.createStatement ();
ResultSet resultat = stmt.executeQuery (
"select * from Station where nomStation=’Passac’");
</xsp:logic>
Quand ce code est exécuté au moment de la transformation par le producteur XSP, la variable resul-
tat est définie et il est possible d’y faire référence ailleurs dans le document, comme par exemple :
<Station>
<nomStation><xsp:expr>resultat.getString (1)</xsp:expr></nomStation>
<capacite><xsp:expr>resultat.getString (2)</xsp:expr></capacite>
<lieu><xsp:expr>resultat.getString (3)</xsp:expr></lieu>
<region><xsp:expr>resultat.getString (4)</xsp:expr></region>
<tarif><xsp:expr>resultat.getString (5)</xsp:expr></tarif>
</Station>
Il reste une dernière particularité dans ce document, les instructions de traitement placée dans le pro-
logue :
<?cocoon-process type="xsp"?>
<?cocoon-process type="xslt"?>
<?xml-stylesheet href="Promotion.xsl" type="text/xsl"?>
Ces instructions indiquent que deux transformations successives doivent être appliquées. La première
est un traitement des instructions XSP. Tout se passe en fait – au moins conceptuellement – comme dans
le cas de XSLT. Le document est transformé en arbre DOM, puis le processeur XSP regarde les nœuds de
l’espace de nom xsp:, évalue leur contenu (en l’occurrence en exécutant le code Java qui s’y trouve) et
remplace le nœud par les données caractères produites par cette exécution.
7.3. XML DYNAMIQUE 233
Ensuite l’élément racine p xsp:page q est supprimé. L’élément p xsp:page q ne peut avoir qu’un
seul élément littéral fils, qui devient l’élément racine après cette suppression. Dans notre cas c’est p Promotion q
qui devient donc l’élément racine.
Le document obtenu est alors soumis à une seconde transformation, avec XSLT cette fois, définie par
le programme Promotion.xsl. Il faut souligner que la séquence XSP-XSLT n’est pas une obligation et que
toute manipulation peut être appliquée à un document issu d’une évaluation XSP.
<?cocoon-process type="xslt"?>
<?xml-stylesheet href="PromotionXSP.xsl" type="text/xsl"?>
<Promotion auteur="Jules"
xmlns:BDS="http://cortes.cnam.fr/Station">
<Description>
Nous proposons une réduction de <Reduction>25</Reduction>
pourcent, restreinte à la période du
<Periode> <Debut>Septembre 2001</Debut>
au <Fin>Octobre 2001</Fin> </Periode> pour tous
les séjours dans certaines stations de vacances.
L’automne est une saison <Important>merveilleuse</Important>
pour reprendre des forces à la montagne. Vous pourrez
profiter du calme,
d’une nature aux couleurs chatoyantes, et d’un contact
privilégié avec l’autochtone. <Important>Attention
le nombre de places offertes est limité.</Important>
</Description>
<BDS:Station nom="Passac"/>
</Promotion>
Cette fois il n’y a plus de JavaṪout le code qui apparaissait dans la version précédente est maintenant
réduit à :
<BDS:Station nom="Passac"/>
Il s’agit d’un élément XML très simple, appartenant à un espace de nom « BDS: » qui désigne notre
librairie de balises, et contenant un attribut qui permet de paramétrer la station que l’on souhaite obtenir.
La signification de cet élément est : « insère ici, à chaque fois que ce document est utilisé, une description
XML de la station dont le nom est donné dans l’attribut nom. ». Cette insertion est dynamique : si la base
de données change, le contenu du document reflètera ce changement dès qu’on y accèdera à nouveau.
Par quelle miracle est-on passé d’un document très compliqué avec du code Java à un document très
simple sans aucune instruction de programmation ? Et bien tout simplement (!) en ajoutant une nouvelle
étape de transformation pour traiter la balise dynamique. Cette étape devient la première du processus de
transformation, et elle consiste à recopier le document tel quel, sauf la balise dynamique qui est remplacée
par le code java que nous avons vu tout à l’heure. Dans Cocoon, cette première transformation est prise en
charge par XSLT. Voici le programme PromotionXSP.xsl qui traite notre balise dynamique :
234 CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:BDS="http://cortes.cnam.fr/Station">
<xsl:template match="Promotion">
<xsl:processing-instruction name="cocoon-process">
type="xsp"
</xsl:processing-instruction>
<xsl:processing-instruction name="cocoon-process">
type="xslt"</xsl:processing-instruction>
<xsl:processing-instruction name="xml-stylesheet">
href="Promotion.xsl" type="text/xsl"
</xsl:processing-instruction>
</xsl:template>
<xsl:template match="BDS:Station">
<xsp:logic>
DriverManager.registerDriver(new org.gjt.mm.mysql.Driver());
// Connection à la base
Connection conn = DriverManager.getConnection
("jdbc:mysql://localhost/Station",
"visiteurStation", "mdpVisiteur");
Statement stmt = conn.createStatement ();
ResultSet resultat = stmt.executeQuery (
"select * from Station "
+ "where nomStation=’<xsl:value-of select=’@nom’/>’");
// Affichage du résultat
resultat.next ();
<Station>
<nomStation><xsp:expr>resultat.getString (1)</xsp:expr></nomStation>
<capacite><xsp:expr>resultat.getString (2)</xsp:expr></capacite>
<lieu><xsp:expr>resultat.getString (3)</xsp:expr></lieu>
<region><xsp:expr>resultat.getString (4)</xsp:expr></region>
<tarif><xsp:expr>resultat.getString (5)</xsp:expr></tarif>
</Station>
</xsp:logic>
</xsl:template>
</xsl:template>
</xsl:stylesheet>
Puisque vous êtes maintenant familiarisé avec XSLT, vous pouvez constater que ce programme ap-
pliqué au document PromotionXSPLib.xml (page 233) va créer un document résultat identique à Promo-
tionXSP.xml (page 230). Voici quand même quelques explications pour vous faciliter la tache :
r les instructions de traitement de PromotionXSP.xml sont obtenue avec xsl:processing-intruction :
<xsl:processing-instruction name="cocoon-process">
type="xsp"
</xsl:processing-instruction>
r les règles du programme XSLT recopient le document source : notez que la dernière a une priorité
égale à -1 ;
r la règle avec le pattern BDS:Station est prise en priorité quand le nœud correspondant à la balise
dynamique est traité : c’est alors le code Java qui est inséré dans le résultat ;
r remarquez enfin que le nom de la station à rechercher dans la requête SQL est pris dans le document
source grâce à xsl:value-of. Il s’agit de l’attribut nom de la balise dynamique :
Il reste à appliquer au document résultat les deux transformations déjà étudiées, l’une avec XSP, et
l’autre avec XSLT, pour obtenir la version finale.
Il existe dans Cocoon des mécanismes qui permettent d’associer automatiquement à un espace de nom
comme BDS des programmes XSLT qui vont remplacer un élément par le code Java correspondant. Nous
ne décrivons pas ces mécanismes qui dépassent le cadre de ce livre, d’autant que notre but est maintenant
atteint. Nous avons :
1. un document XML simple et intégrant des balises « dynamiques » de manière transparente : le ges-
tionnaire de contenu peut se contenter de connaître la signification de ces balises, sans avoir à com-
prendre leur fonctionnement ;
2. un programme XSLT/XSP qui contient la « logique » de l’application : nous avons montré l’exemple
d’un accès base de données avec Java, mais on peut imaginer toutes sortes d’architectures, avec par
exemple l’utilisation de beans comme celui que nous avons créé pour nos exemples JSP ;
3. enfin un ou plusieurs programmes XSLT pour transformer le document final en HTML, WML ou
XSL-FO/PDF.
L’utilisation de XSP reste relativement complexe, ce qui induit un coût de conception et de développe-
ment non négligeable si on veut aboutir à une vraie séparation des points de vue. De plus les performances
posent problème. La version 2.0 de Cocoon va probablement s’attacher à corriger ces défauts.
7.3.2 XSQL
L’utilitaire XSQL fait partie du XML Development Kit (XDK) d’Oracle, un ensemble d’outils de traitement
de documents XML comprenant les parseurs DOM et SAX, un processeur XSLT, et de nombreux packages
Java. Contrairement à XSP qui est un langage généraliste destiné à permettre l’inclusion de n’importe quel
code Java dans des pages XML, XSQL se concentre sur l’extraction de données d’une base Oracle avec
le langage SQL (quelque peu étendu), sur l’inclusion au format XML du résultat de la requête dans un
document, suivie enfin d’une transformation XSLT. XSQL est donc de ce point de vue plus limité que XSP,
mais s’avère en contrepartie beaucoup plus simple à utiliser.
236 CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
XSQL fournit la plupart des fonctionnalités que nous étudions depuis le début de ce chapitre et constitue
donc un excellent exemple d’une réalisation pratique d’un environnement de publication web intégrant une
base de données. On y trouve :
1. le paramétrage du format de sortie XML, semblable à ce qu’offre notre petit outil ExportXML.java ;
2. la possibilité de paramétrer les requêtes avec des variables provenant par exemple d’une requête
HTTP ;
3. un enchaînement du processus d’intégration des données issues de la base avec une transformation
XSLT ;
4. enfin une cloisonnement (presque) complet entre les couches SQL, XML et XSLT.
Nous commençons par quelques exemples simples de documents XSQL avant de décrire les principales
options fournies par cet outil.
<Promotion auteur="Jules">
<Description>
Nous proposons une réduction de <Reduction>25</Reduction>
pourcent, restreinte à la période du
<Periode> <Debut>Septembre 2001</Debut>
au <Fin>Octobre 2001</Fin> </Periode> pour tous
les séjours dans certaines stations de vacances.
L’automne est une saison <Important>merveilleuse</Important>
pour reprendre des forces à la montagne. Vous pourrez
profiter du calme,
d’une nature aux couleurs chatoyantes, et d’un contact
privilégié avec l’autochtone. <Important>Attention
le nombde de places offertes est limité.</Important>
</Description>
<xsql:query connection="connexionVisiteur"
xmlns:xsql="urn:oracle-xsql">
SELECT * FROM Station WHERE nomStation=’Passac’
</xsql:query>
</Promotion>
On retrouve les même principes déjà étudiés pour XSP (et, plus globalement, pour des langages de
production de HTML dynamique comme les JSP). Ce document est un document XML, certaines balises
étant associées à un espace de nom particulier (ici xsql). Le processeur qui traite ce document reconnaît
ces balises comme des instructions, déclenche un traitement et crée un nouveau document dans lequel les
balises « dynamiques » sont remplacées par le résultat de ce traitement.
Le principal élément de XSQL est p xsql:query q . Son contenu est une requête SQL (ici l’interrogation
de la table Station), et il peut prendre un ensemble d’attributs pour paramétrer notamment la manière dont
le résultat est mis en forme XML. Les deux attributs utilisés dans ce premier exemple sont :
7.3. XML DYNAMIQUE 237
r connection qui indique le nom de la connexion à utiliser pour accéder à Oracle ; ce nom de
connexion est associé, dans un fichier de paramétrage, au compte utilisateur ainsi qu’à la base à
utiliser ;
r la déclaration de l’espace de nom xmlns.
Ce document peut être traité dans plusieurs des architectures présentées précédemment : avec un pro-
gramme lancé depuis la ligne de commande, avec une servlet, ou dans une page JSP.
En l’absence d’indication sur la mise en forme souhaitée, XSQL produit un document XML de la forme
suivante :
<ROWSET>
<ROW id="1">
<nomStation>Passac</nomStation>
<capacite>400</capacite>
<lieu>Alpes</lieu>
<region>Europe</region>
<tarif>1000.00</tarif>
</ROW>
</ROWSET>
L’élément racine du document est p ROWSET q (ensemble de lignes). Chaque ligne de la table est
représentée par un élément de type ROWSET, automatiquement identifié par un attribut id dont la valeur,
par défaut, est simplement un compteur sur les lignes du résultat. Enfin chaque colonne est également
représentée par un élément.
Ce résultat est intégré dans le document principal. Éventuellement un programme XSLT spécifié par
l’instruction de traitement p ?xml-stylesheet q est alors appliqué pour effectuer une transformation.
Voici maintenant comment utiliser des paramètres provenant d’une requête HTTP. Nous reprenons
notre exemple d’un serveur web permettant d’interroger la base Station en donnant trois paramètres (voir
notre servlet page 223 et son formulaire associé) : le nom de la station à afficher, le nom de l’élément racine
du document, et le nom de l’élément pour chaque ligne. Voici ce service d’interrogation avec XSQL :
<xsql:query connection="connexionVisiteur"
xmlns:xsql="urn:oracle-xsql"
rowset-element="{@racine}"
row-element="{@element}">
</xsql:query>
Les paramètres sont représentés par la syntaxe @nomParam. On peut les intégrer dans les attributs de
p xsql:query q , ou dans la requête SQL elle-même. Cet exemple montre deux nouveaux attributs de
cet élément :
r rowset-element définit le nom de l’élément racine du document produit, sa valeur par défaut
étant ROWSET ;
r row-element définit le nom de l’élément pour chaque ligne du résultat de la requête SQL.
238 CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
Le document ci-dessus renvoie le résultat au format XML, mais on pourrait bien entendu appliquer un
programme de transformation XSLT sur le serveur avant la transmission au client.
XSQL fournit donc une interface très simple entre une base de données et XML. Il existe de nombreuses
autres éléments et options pour choisir par exemple la mise en forme XML, le mode de sérialisation du
document résultat, ou, à l’inverse, le stockage d’un document XML dans Oracle. Nous complétons cette
présentation en décrivant les possibilités de mise en forme du document XML produit par une requête
XSQL.
XSQL fournit des options à plusieurs niveaux pour les options de sorties XML. Tout d’abord l’élément
p xsql:query q utilise un ensemble d’attributs présentés dans le tableau 7.1. Les plus importants ont
été illustrés dans les exemples qui précèdent.
Au niveau de la requête SQL elle-même, on peut agir sur le format du résultat en demandant notamment
une imbrication des éléments. En principe SQL est un langage qui agit sur des tables et produit une table.
Dans une table relationnelle, on trouve des lignes constituée d’attributs dont la valeur est « atomique » (un
entier, une chaîne de caractères). On ne peut pas en théorie dire qu’une valeur est un graphe, une autre table,
ou toute structure un tant soit peu complexe. Il n’existe donc, à aucun niveau, la possibilité de manipuler
une structure hiérarchique.
Oracle fournit deux types d’extensions pour étendre le modèle relationnel : au niveau du modèle, et
au niveau du langage d’interrogation. Au niveau du modèle le SGBD d’Oracle est, depuis la version 8,
« relationnel-objet », et permet d’imbriquer des types de données complexes dans des tables relationnelles.
La mise en forme XML d’une table de ce type reprend exactement l’imbrication du modèle Oracle.
La seconde possibilité apparaît au niveau du langage lui-même, avec l’opérateur CURSOR qui, utilisé
dans la clause SELECT, permet de constituer une résultat de requête qui comprend des attributs simples et
des tables imbriquées. Voici un exemple de requête SQL avec CURSOR :
Dans la clause SELECT, nomStation et capacite sont des valeurs atomiques (des chaînes de
caractères), mais l’évaluation de CURSOR est une table. Ce troisième attribut est nommé activites
avec la clause AS. On obtient donc une table imbriquée dont voici la représentation XML (en nous limitant
à la station Passac).
7.4. PERSPECTIVES 239
<ROWSET>
<ROW num=’1’>
<nomStation>Passac</nomStation>
<capacite>400</capacite>
<activites>
<activites_row num=’1’>
<libelle>Ski</libelle>
<prix>200.00</prix>
</activites_row>
<activites_row num=’2’>
<libelle>Piscine</libelle>
<prix>20.00</prix>
</activites_row >
</activites>
</ROW>
</ROWSET>
On peut utiliser plusieurs curseurs, avec plusieurs niveaux d’imbrication, ce qui permet d’obtenir des
documents hiérarchiques XML arbitrairement complexes.
7.4 Perspectives
Comme dans toute situation où des systèmes basés sur des représentations différentes de l’information
doivent coexister, l’association entre XML et une base de données relationnelle, et les conversions néces-
saires aux échanges dans l’un ou l’autre sens, introduisent un niveau de difficulté supplémentaire dans des
architectures logicielles déjà passablement complexes. Nous n’avons parlé ici que de ce qui relève de notre
sujet, l’exportation de données vers XML afin de les intégrer à un système de publication basé sur XSLT.
L’opération inverse, le stockage de documents XML dans une base de données, est au moins aussi déli-
cate, la conversion d’une représentation hiérarchique comme XML vers une représentation relationnelle
« à plat » étant difficile à définir sans perte d’information.
r Performances : les SGBD proposent des structures d’index très performantes pour accéder aux don-
nées.
r Sécurité, transactions, ... : enfin beaucoup d’aspects systèmes liés à la sécurité des données (droits
d’accès, transactions, reprises sur panne) manquent en XML.
Certains systèmes utilisant XML comme format natif semblent en cours d’expérimentation, ce qui per-
mettra(it) de régler certains de ces problèmes. À l’heure actuelle il semble cependant plus raisonnable
d’utiliser une base de données classique dès qu’on a besoin de gérer un ensemble de données de taille con-
séquente, et de prendre plutôt XML comme format d’échange ou de « vue » sur les données pour certaines
applications. Il est cependant juste de signaler que certains travaux menés par le W3C visent à lever cer-
taines des limitations mentionnées ci-dessus. Nous en citons brièvement deux : l’introduction d’un typage
plus précis avec XML Schema, et le langage d’interrogation XML Query.
Sommaire
241
242 CHAPTER 8. UN SERVEUR DE PUBLICATION XML
Appendix A
L’environnement XML/Apache
Sommaire
Nous décrivons dans ce chapitre l’installation et l’utilisation des principaux outils du projet XML/Apache1.
Ce projet a pour principaux objectifs de fournir des logiciels et boîtes à outils de grande qualité, gratuits
et développés en suivant la philosophie Open Source : une communauté de programmeur contribue, via
le Web, à la conception, à l’élaboration et à l’amélioration de produits dont les sources sont librement
disponibles. Le site de référence est :
http://xml.apache.org
Il existe maintenant une très grande quantité de logiciels intégrant des fonctionnalités de traitement
XML, et nous ne prétendons pas que ceux de XML/Apache sont les meilleurs. Cet environnement offre
simplement l’avantage d’être gratuit, de s’installer relativement facilement, et de s’appuyer sur les librairies
XML de la fondation Apache, largement utilisées et qui suivent de près l’évolution des normes du W3C
(Xalan par exemple se présente comme un processeur XSLT totalement compatible avec la norme XSLT
1.0). Tous les exemples de ce livre ont été testés avec ces outils.
1 Ne pas confondre avec le serveur web Apache, qui n’est que l’un des logiciels – le plus connu – diffusé par la fondation Apache
243
244 APPENDIX A. L’ENVIRONNEMENT XML/APACHE
1. Xerces, un ensemble d’outils d’analyse avec notamment des parseurs SAX et DOM ;
Les parseurs SAX et DOM fournis par Xerces sont utilisés par les autres sous-projets pour analyser
et sérialiser les documents XML : nous ne détaillerons pas l’interface (API) qui est disponible en Java et
en C++. En revanche nous décrivons l’installation, et quelques exemple d’application de Xalan, qui peut
s’utiliser indépendamment de tout autre outil pour les tâches suivantes :
2. transformation « statique » de documents XML avec XSLT : un exemple de production statique est
la génération de pages HTML destinées à être placées sur un serveur web ;
Xalan s’installe et s’utilise extrêmement facilement et peut vous fournir en peu de temps un environ-
nement de test.
Les choses sont un peu différentes pour Cocoon qui se présente sous la forme d’une servlet, autrement
dit un objet Java intégré à un serveur web qui prend en charge des requêtes HTTP. Il s’agit donc d’un outil
spécifiquement orienté vers la publication web. À l’heure où ces lignes sont écrites la version stable de
Cocoon est la 1.8, mais une version 2 – longtemps attendue – est en phase béta et devrait être prochainement
disponible. Cocoon en association avec un serveur web comme Tomcat fournit un environnement tout à
fait satisfaisant pour expérimenter la publication à base de documents XML/XSLT.
Enfin nous concluons le chapitre par l’installation d’une architecture plus complexe incluant le serveur
web Apache associé à PHP et à une base de données, et communiquant avec Tomcat pour les services
XML. Voici les sites web où vous pourrez récupérer tous les logiciels :
r pour le serveur web Apache, c’est www.apache.org ;
r pour Tomcat, c’est java.apache.org ;
r pour PHP, c’est www.php.net.
Toutes les installations sont décrites en prenant comme environnement de référence un serveur Linux.
Les outils étant écrit en Java, il n’y a pas de grandes différences quand on veut utiliser un serveur sous
Windows : nous citons ces différences au fur et à mesure.
A.2 Xalan
Xalan est une librairie Java, accompagnée de plusieurs programmes utilitaires ou de démonstration. La
librairie propose une API conforme à la – récente – norme TrAX qui définit l’interface d’un processeur
de transformation XSLT. Le fait de définir une interface commune permet (en principe...) de passer d’un
processeur à un autre sans avoir à réécrire toute une application.
Xalan s’appuie en principe sur les parseurs Xerces mais il peut – toujours en principe – utiliser d’autres
parseurs. Il existe en Java et C++ : c’est la version Java que nous décrivons ci-dessous.
A.2. XALAN 245
cp jdk-1_2_2_007-linux-i386.tar.gz /usr/local
tar xvfz jdk-1_2_2_007-linux-i386.tar.gz /usr/local
Ces commandes créent un répertoire jdk-1.2.2. Il reste à modifier deux variables d’environnement pour
que le JDK soit disponible. Placez par exemple les commandes suivantes dans votre fichier .bashrc :
export JAVA_HOME=jdk-1.2.2
export PATH=$JAVA_HOME/bin:$PATH
Sous Windows il suffit de suivre exatement la même démarche, en définissant les variables soit dans le
fichier autoexec.bat, soit avec le panneau de contrôle, option Système.
À partir de là vous pouvez compiler du code Java avec la commande javac, et exécuter un programme
Java avec la commande java. Au cas où vous ne connaitriez pas du tout Java, rassurez-vous : il n’y a pas
besoin d’écrire du code.
cd /usr/local
cp xalan-XXX.tar.gz .
tar xvfz xalan-XXX.tar.gz
Il ne reste plus qu’à placer les deux archives xerces.jar et xalan.jar dans la variable d’environnement
CLASSPATH. Voici un exemple (toujours sous Linux) : utilisez les commandes équivalentes pour Win-
dows.
export CLASSPATH=/usr/local/xalan-XXX/bin/xalan.jar:$CLASSPATH
export CLASSPATH=/usr/local/xalan-XXX/bin/xerces.jar:$CLASSPATH
C’est prêt ! Il est maintenant possible de compiler des programmes utilisant l’API de Xalan.
javac ApplyXPath.java
On obtient un fichier ApplyXPath.class qui peut être exécuté par une machine java avec la commande 2 :
java ApplyXpath fichierXML expressionXPath
Vous pouvez tester vos expressions XPath. Voici quelques exemples, en utilisant le fichier ExArbreX-
Path.xml que nous avons utilisé dans le chapitre 2 pour présenter XPath (voir page 87) et que vous pouvez
trouver sur notre site (le « % » indique le prompt du shell Linux) :
Noter que toutes les expressions XPath sont basées sur des chemins absolus.
</h1>Science-fiction,
<i>Etats Unis</i>,
1979</p>
<p>
Mis en scène par <b>Ridley Scott</b>
<h3>Résumé</h3>Près d’un vaisseau
spatial échoué sur une lointaine
planète, des Terriens en mission
découvrent de bien étranges
"oeufs". Ils en ramènent un à bord,
ignorant qu’ils viennent d’introduire parmi eux
un huitième passager particulièrement
féroce et meurtrier.
</p>
</body>
Ce mode d’utilisation de Xalan peut être utile pour tester des programmes XSLT, ainsi que pour en-
gendrer des sites HTML « statiques » et éviter le coût de transformations dynamiques. De très nombreuses
autres options sont disponibles pour contrôler le format de sortie, passer des paramètres, etc. La définition
de paramètre de premier niveau par exemple s’effectue avec l’option :
-PARAM nomParam valeurParam
Si le programme XSLT appelé a un élément de premier niveau p xsl:param name=’nomParam’ q ,
la valeur valeurParam lui sera affectée. Nous renvoyons à la documentation de cet utilitaire (fichier
docs/commandline.html) pour plus d’informations.
A.3 Cocoon
Cocoon doit être associé à un serveur web capable de gérer les servlets. Nous proposons l’utilisation de
Tomcat, un serveur web entièrement écrit en java et également fourni par la fondation Apache. Cocoon
peut cependant fonctionner en association avec tout serveur compatible avec la version 2.0 des servlets :
nous vous renvoyons aux spécifications disponibles sur le site http://java.sun.org et à la documentation de
Cocoon pour toute installation avec un serveur autre que Tomcat.
A.3.1 Tomcat
Tomcat est un servlet container ce qui correspond, pour dire les choses brièvement, à un serveur web
capable de charger des objets java (les servlets) qui vont prendre en charge certaines requêtes HTPP et
engendrer dynamiquement le document HTML constituant la réponse.
248 APPENDIX A. L’ENVIRONNEMENT XML/APACHE
Tomcat est un sous-projet de Jakarta/Apache, le projet de la fondation Apache dédié aux environ-
nements Java, comme Cocoon est un sous-projet de XML/Apache. Vous pouvez récupérer Tomcat sur le
site
http://jakarta.apache.org
Si vous n’avez pas l’intention de modifier le code source (!), l’archive contenant les exécutables suffira.
Au moment où ces lignes sont écrites la version de référence est la 3.2.2, et le fichier archive s’appelle
jakarta-tomcat-3.2.2.tar.gz.
Comme d’habitude, il faut décompacter l’archive quelque part.
cd /usr/local
tar xvfz jakarta-tomcat-3.2.2.tar.gz
Et maintenant une bonne nouvelle : Tomcat est prêt ! On peut simplement le lancer en allant dans
jakarta-tomcat-3.2.2 et en entrant :
bin/startup.sh
Si la variable JAVA_HOME est correctement définie, Tomcat devrait se lancer sans problème. Cela
étant il est sans doute préférable de définir la variable d’environnement TOMCAT_HOME qui doit contenir
le chemin d’accès vers jakarta-tomcat-3.2.2 :
export TOMCAT_HOME=/usr/local/jakarta-tomcat-3.2.2
Tomcat est un serveur web. Par défaut il est lancé sur le port 8080 (vous pouvez changer cette valeur
dans le fichier conf/server.xml). Après le startup, on peut donc accéder à Tomcat à l’URL http://localhost:8080.
La page d’accueil propose de tester quelques exemples de servlets et de pages JSP.
Pour arrêter Tomcat, on exécute simplement :
bin/shutdown.sh
Le script tomcat.sh est lancé par startup et shutdown avec les options respectives start et stop. On peut
donc s’en servir comme script d’initialisation de Tomcat au lancement de Linux. Nous sommes maintenant
prêts pour installer Cocoon.
A.3.2 Cocoon
Cocoon est simplement (!) une servlet qui va être appelée par Tomcat. Récupérer Cocoon, sous la forme
habituelle d’un fichier .tar.gz, sur le site httpd://xml.apache.org et l’installer (par exemple) dans /usr/local/.
On obtient, en décompressant l’archive, un répertoire cocoon-1.8 (à la version près).
Pour installer Cocoon dans Tomcat, il faut commencer par quelques copies de fichier. Notez que
Cocoon est livré avec ses propres versions de xalan.jar et xerces.jar de manière à éviter les problèmes
consécutifs à des incompatibilités de version. Il est donc recommandé de s’en tenir aux librairies fournies
et de ne pas chercher à les mélanger avec d’autres.
1. Copier tous les .jar de COCOON_HOME/lib dans TOMCAT_HOME/lib ; copier également CO-
COON_HOME/bin/cocoon.jar dans TOMCAT_HOME/lib
Tomcat charge automatiquement toutes les classes dont le fichier .jar se trouve dans TOMCAT_HOME/lib,
ce qui évite d’avoir à configurer ce chargement.
2. Il faut maintenant ajouter un « contexte » à Tomcat pour lui indiquer quand il doit utiliser Cocoon.
Pour cela, ajouter la ligne suivante dans le fichier TOMCAT_HOME/conf/server.xml.
Maintenant, toute URL commençant par /cocoon sera traitée par Cocoon, et les fichiers seront pris
dans TOMCAT_HOME/webapps/cocoon qui tient lieu d’adresse relative.
mkdir TOMCAT_HOME/webapps/cocoon
mkdir TOMCAT_HOME/webapps/cocoon/WEB-INF
cp $COCOON_HOME/src/WEB-INF/web.xml
$TOMCAT_HOME/webapps/cocoon/WEB-INF
cp $COCOON_HOME/conf/cocoon.properties
$TOMCAT_HOME/webapps/cocoon/WEB-INF
Important : il faut éditer le fichier web.xml et indiquer un chemin d’accès relatif vers le fichier
cocoon.properties. Ce chemin doit être WEB-INF/cocoon.properties. Voici la partie du fichier, avec
la bonne valeur.
<servlet>
<servlet-name>org.apache.cocoon.Cocoon</servlet-name>
<servlet-class>org.apache.cocoon.Cocoon</servlet-class>
<init-param>
<param-name>properties</param-name>
<param-value>WEB-INF/cocoon.properties</param-value>
</init-param>
</servlet>
http:localhost:8080/cocoon/samples
Bien entendu vous pouvez également récupérer les exemples de ce livre et les tester dans votre propre
environnement.
totalité un serveur web sur Tomcat. Si Tomcat est parfait pour gérer des servlets et leurs dérivés (JSP), il est
loin d’être aussi souple, aussi paramétrable et aussi ouvert qu’un serveur web classique comme Apache.
Une solution robuste et ouverte à de très nombreux outils de développement consiste donc à faire
collaborer un serveur comme Apache et Tomcat, selon une architecture illustrée dans la figure A.1. Deux
serveurs web, Apache et Tomcat, tournent en parallèle, chacun en écoute sur le port réseau qui leur est
affecté. Dans notre schéma Apache est en écoute sur le port HTTP standard 80, tandis que Tomcat est
associé au port 8080.
Requêtes HTTP
80 8080
Transfert requêtes
Apache Servlets Tomcat
Cocoon Servlet A
Toutes les requêtes peuvent être adressées au port 80 : s’il s’agit de demandes HTTP pour des docu-
ments HTML ou PHP, Apache les prend en charge sans recourir à Tomcat. En revanche des demandes qui
impliquent les services basés sur des servlets sont transmis par Apache à Tomcat.
C’est l’installation de cette architecture que nous décrivons ci-dessous. Encore une fois il est tout à fait
possible de s’arrêter à l’installation de Tomcat seul, et de s’en servir comme environnement de test.
Toutes les indications qui suivent ont été testées sous une version 7.0 de la distribution SuSe de Linux.
A.4.1 Compilateur
Éventuellement, il faut récupérer le compilateur GNU C++ si vous ne l’avez pas. On peut le trouver par ex-
emple à http://www.gnu.ai.mit.edu/software/gcc/gcc.html ou sur un site miroir GNU. Voici les commandes
permettant d’installer le compilateur :
1. Récupérez par le fichier gcc-2.8.1.tar.gz (ou version supérieure) et placez le dans /usr/local/src.
2. Décompressez l’archive, et extrayez son contenu :
% gunzip gcc-2.8.1.tar.gz
% tar xvf gcc-2.8.1.tar
Ce qui crée un nouveau répertoire gcc-2.8.1 dans /usr/local/src.
3. Placez vous dans ce répertoire avec la commande
% cd gcc-8.2.1
4. Entrez successivement les commandes suivantes :
% ./configure
% make
% make install
C’est tout ! Par défaut, l’installation se fait dans /usr/local/bin. Il faut donc mettre ce répertoire
au début de la variable PATH, avec une commande qui dépend du shell employé, ici pour les shell de type
Bourne comme bash :
% export PATH=/usr/local/bin:$PATH
Vous pouvez vérifier alors que tout s’est bien passé avec la commande gcc -v qui devrait vous afficher
gcc version 2.8.1 (ou version supérieure).
A.4. INTÉGRATION APACHE/TOMCAT/COCOON 251
A.4.2 Apache
Le plus simple d’installer Apache avec les modules dynamiques (DSO), ce qui évite de tout recompiler
à chaque fois. Commencez par décompacter l’archive apache_1.3.20 (ou une version ultérieure) quelque
part, disons dans /usr/local/src/apache_1.3.20. Placez le fichier archive dans /usr/local/src et entrez la
commande suivante :
% tar xvfz apache_1.3.20.tar.gz
Tous les fichiers vont être extraits de l’archive et placés dans le répertoire /usr/local/src/apache_1.3.20.
Allez alors dans ce répertoire et entrez les commandes suivantes :
Apache est maintenant prêt et installé dans /usr/local/apache. Entrez les commandes suivantes : % cd
/usr/local/apache
% ./bin/apachectl start
httpd est lancé et se met en écoute sur le port 80 (à moins que vous n’ayez changé la valeur de Port
dans httpd.conf ). Pour lancer et stopper Apache avec Linux, copiez le script apachectl dans /etc/rc.d et
créez dans /etc/rc.d/rc3.d deux liens comme suit :
ln -s ../apachectl S33Apache
ln -s ../apachectl K33Apache
A.4.3 PHP
Pour ajouter le module PHP (optionnel !), entrez les commandes suivantes dans le répertoire racine des
sources de PHP (on peut ajouter l’option --with-mysql si vous avez installé MySQL) :
./configure --with-apxs=/usr/local/apache/bin/apxs
make
make install
Il ne reste plus qu’à décommenter les lignes suivantes dans httpd.conf pour que PHP soit disponible :
Maintenant si MySQL ou un autre SGBD est aussi installé, on dispose d’un environnement complet
pour faire de la programmation web et base de données avec PHP. On se servira de cet environnement
pour toutes les requêtes HTTP demandant du HTML ou du PHP, et on redirigera vers Tomcat toutes les
demandes de type XML/XSLT ou JSP.
A.4.5 Et Cocoon ?
Il reste à indiquer à Apache ce qu’il faut faire en présence d’un fichier XML. Bien entendu il faut traiter ce
XML avec Cocoon. Voici les lignes à ajouter dans le fichier mon-mod-jk.conf.
Alias /cocoon TOMCAT_HOME/webapps/cocoon
<Directory "TOMCAT_HOME/webapps/cocoon">
Options Indexes FollowSymLinks
</Directory>
JkMount /cocoon/*.xml ajp12
AddType text/xml .xml
Référence XPath/XSLT
Sommaire
Cette annexe est consacrée aux éléments et aux fonctions XLST. Elle est conçue et présentée comme
une référence, venant compléter les chapitres « explicatifs » qui précèdent. Contrairement aux autres
chapitres de ce livre qui suivaient un ordre basé sur la thématique et la mise en œuvre des fonctionalités
XSLT, la présentation suit l’ordre alphabétique pour faciliter la recherche. Nous donnons une description
la plus concise et informative possible de chaque élément, en renvoyant si besoin est à la page du livre qui
développe des explications et des exemples sur son utilisation.
Vous pouvez aussi vous aider de l’annexe pour trouver toutes les pages de ce livre qui traitent d’un
élément XSLT donné, les pages relatives à la référence étant indiquées en gras.
xsl:apply-imports
Syntaxe
p xsl:apply-imports/ q
253
254 APPENDIX B. RÉFÉRENCE XPATH/XSLT
Désignation Description
Expression Toute expression XPath
QName Le terme Qualified name (QName) désigne tout
nom d’élément XML valide, comme par exemple
xbook:monEl. Un QName se compose d’un
préfixe, optionnel (en l’occurrence xbook), cor-
respondant à un espace de nom déclaré, et d’un
nom local, obligatoire (ici monEl)
NCName Cette catégorie désigne tout nom XML valide,
utilisable par exemple por un préfixe, ou un nom
local, ou un nom d’élément.
Pattern Les patterns appartiennent au sous-ensemble
des expressions XPath autorisées dans l’attribut
match de l’élément xsl:template : voir
page 119
URI Un Uniform Resource Identifier. Il prend le plus
souvent la forme d’un fichier local, ou d’une
adresse HTTP
Corps de règle Un corps de règle (voir page 111) est toute com-
binaison de texte littéral, d’éléments littéraux
(c’est-à-dire non interprétés par le processeur) et
d’instructions XSLT correctement formées. Un
corps de règle définit un fragment du document
XML à instancier au cours du traitement
Description
Quand on importe un programme XSLT dans un autre, les règles du programme importé ont une priorité in-
férieure à celles du programme importateur. Une des conséquences est que la règle importée est remplacée,
ce qui n’est pas toujours l’effet souhaité. On peut très bien vouloir utiliser cette règle, et la compléter
par d’autres instructions. Dans ce cas on utilise, dans le corps de la règle, l’instruction xsl:apply-
imports.
Cette fonctionnalité s’apparente au mécanisme de surcharge de méthode dans la programmation orientée-
objet. Notez qu’il est souvent possible d’obtenir le même résultat avec xsl:call-template.
XSLT prévoit la possibilité de passer des paramètres à la règle importée en plaçant dans son contenu
une ou plusieurs instructions xsl:with-param (voir page 282).
Exemple
Voici par exemple un programme XSLT qui contient une règle de présentation standard pour le contenu
d’un élément p PERSONNE q (nom, prénom, date de naissance) :
Exemple 107 Personne.xsl : Un règle générique pour les personnes
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head><title>Ficher personne</title></head>
<body bgcolor="white">
<xsl:apply-templates/>
B.1. ÉLÉMENTS XSLT 255
</body></html>
</xsl:template>
<xsl:template match="PERSONNE">
<b> Prénom</b> : <xsl:value-of select="PRENOM"/>,
<b> Nom </b> :<xsl:value-of select="NOM"/>,
Né le <i><xsl:value-of select="NAISSANCE"/></i>
</xsl:template>
</xsl:stylesheet>
Maintenant supposons qu’on se trouve avec une occurrence d’un élément de type PERSONNE plus
détaillée, comme celle du fichier ci-dessous.
<PERSONNE NOSS="17517652987">
<NOM>Doe</NOM>
<PRENOM>John</PRENOM>
<NAISSANCE>15:10:1981</NAISSANCE>
<POSTE>Directeur Informatique</POSTE>
<SALAIRE>50 000</SALAIRE>
</PERSONNE>
Au lieu de redéfinir complètement la règle du programme Personne.xsl, on peut l’importer, puis l’utiliser
avec xsl:apply-import inséré dans la nouvelle règle de traitement des éléments de type PERSONNE
<xsl:import href="Personne.xsl"/>
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head><title>Ficher personne</title></head>
<body bgcolor="white">
<xsl:apply-templates/>
</body></html>
</xsl:template>
<xsl:template match="PERSONNE">
<xsl:apply-imports/>
</xsl:stylesheet>
Une règle est utilisée dans xsl:apply-imports si et seulement si elle s’applique au nœud courant
et si elle a été importée. Une solution simple pour être sûr qu’une règle importée et une règle locale
s’appliquent au même nœud est bien entendu qu’elles aient toutes deux le même pattern.
256 APPENDIX B. RÉFÉRENCE XPATH/XSLT
xsl:apply-templates
Syntaxe
p xsl:apply-templates
select=Expression
mode=QName q
( p xsl:with-param q | p xsl-sort q )s
p /xsl:apply-templates q
Description
Cette instruction désigne tous les nœuds à traiter au sein du document source (ou, éventuellement, d’un
document référencé par la fonction document()). Le processeur détermine alors pour chaque élément sélec-
tionné la règle à appliquer. Les deux attributs possibles sont optionnels. L’attribut select est une expres-
sion XPath dont l’évaluation doit donner un ensemble de nœuds. Si cette expression est relative, le nœud
contexte est le nœud du document source qui a instancié la règle contenant xsl:apply-templates
(voir page 81). Si select est absent, alors les nœuds sélectionnés sont les fils du nœud courant (autrement
la valeur par défaut de select est child::node()).
L’attribut mode indique un critère complémentaire pour la sélection des règles à appliquer. Si cet
attribut est présent avec ue valeur nomMode, seules les règles xsl:template ayant elles-mêmes un
attribut mode égale à nomMode seront prises en compte (voir page 126).
On peut passer des paramètres aux règles déclenchées importée en plaçant dans le contenu de xsl:apply-
templates un ou plusieurs instructions xsl:with-param (voir page 282).
Par défaut les éléments sélectionnés sont pris en compte dans l’ordre de parcours du document. En
introduisant un ou plusieurs éléments xsl:sort, on peut changer cet ordre : voir page 277.
Exemples
xsl:apply-templates est bien entendu l’un des éléments les plus utilisés. De très nombreux exemple
ont été donnés dans ce livre : voir notamment la section consacrée au règles, page 119 du chapitre 3.
xsl:attribute
Syntaxe
p xsl:atrribute
name=QName
namespace=URI q
Corps de règle p /xsl:attribute q
Description
Cet élément déclenche l’ajout d’un attribut dans l’élément courant du document résultat. Cet élément
courant peut être soit un élément littéral, soit un élément créé avec l’instruction xsl:element, soit un
élément du document source copié avec xsl:copy. L’élément xsl:attribute doit être instancié
immédiatement après la balise ouvrante de l’élément courant.
Le contenu dexsl:attribute définit la valeur de l’attribut. C’est un corps de règle qui peut contenir
d’autres instructions XSLT, et doit impérativement engendrer un nœud de type Text (donc sans marquage).
Il est possible de calculer les deux attributs, name et namespace, en utilisant des expressions XPath
encadrées par { }, désignées par le terme (attribute value template). Ce même mécanisme est applicable
pour placer directement les attributs dans l’élément, sans avoir donc besoin d’utiliser xsl:attribute.
Voici quelques exemples pour illustrer ces possibilités.
B.1. ÉLÉMENTS XSLT 257
Exemples
Reprenons le document JohnDoe.xml, page 255. Il contient un élément racine de type PERSONNE, avec
des fils NOM et PRENOM. Le programme suivant crée un document contenant un seul élément, les fils étant
transformés en attributs.
<xsl:template match="PERSONNE">
<PERSONNE>
<xsl:attribute name="NOM">
<xsl:value-of select="NOM"/>
</xsl:attribute>
<xsl:attribute name="PRENOM">
<xsl:value-of select="PRENOM"/>
</xsl:attribute>
</PERSONNE>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="PERSONNE">
<PERSONNE NOM="{NOM}" PRENOM="{PRENOM}"/>
</xsl:template>
</xsl:stylesheet>
Alors quel est l’intérêt d’utiliser xsl:attribute ? Et bien dans tous les cas où on ne veut pas
l’associer à un élément littéral (par exemple quand l’élément est engendré par xsl:element), ou quand
le nom ou la valeur de l’attribut sont calculés par des expressions complexes.
Voici un exemple où on veut ajouter un attribut AGE à l’élément p PERSONNE q dans le document
résultat, en calculant la valeur de cet attribut par différence entre l’année courante (disons que c’est 2002)
et l’année de naissance.
<xsl:template match="PERSONNE">
<PERSONNE NOM="{NOM}" PRENOM="{PRENOM}">
<xsl:attribute name="AGE">
<xsl:value-of select="2002 - substring(NAISSANCE/text(),7)"/>
258 APPENDIX B. RÉFÉRENCE XPATH/XSLT
</xsl:attribute>
</PERSONNE>
</xsl:template>
</xsl:stylesheet>
On pourrait certes mettre l’expression directement dans la balise p PERSONNE q , comme pour NOM,
mais on arrive rapidement aux limites de cette solution quand on utilise pour calculer la valeur de l’attribut
des tests, des itérations, etc.
xsl:attribute-set
Syntaxe
p xsl:attribute-set
name=QName
use-attribute-sets=QNames q
p xsl:attribute qts
p /xsl:attribute-set q
Description
Cet élément permet de grouper des définitions d’attributs, et de nommer les groupes ainsi constitués afin
de pouvoir les affecter d’un bloc à un élément. Cette fonctionnalité est assez proche des feuilles de style
(CSS) dans lesquelles on factorise des propriétés de mise en forme de certains éléments HTML.
L’attribut name est obligatoire : il sert à référencer le groupe d’attribut. Le second attribut, use-
attribute-set, permet de composer récursivement un groupe d’attributs à l’aide d’autres groupes. La
valeur de attruse-attribute-set doit être une liste de noms de groupe d’attributs, séparés par des espaces. On
peut ainsi par exemple définir un groupe pour les attributs relatifs aux polices de caractères, un autre pour
les couleurs, et les grouper tous deux dans un troisième groupe.
Il est possible de trouver plusieurs groupes d’attributs avec le nom dans un programme XSLT. Dans ce
cas le processeur fusionne les deux listes.
Pour affecter un groupe d’attribut à un élément, on utiliser l’attribut use-attribute-sets des
éléments xsl:copy ou xsl:element : voir pages 264 et 267, ou introduire un attribut xsl:use-
attribute-sets dans un élément littéral.
Exemple
Le programme suivant définit un groupe nommé MonStyle avec des attributs de présentation reconnus
par HTML. Ce groupe est ensuite appliqué à l’élément p body q .
<xsl:attribute-set name="MonStyle">
<xsl:attribute name="bgcolor">white</xsl:attribute>
<xsl:attribute name="font-name">Helvetica</xsl:attribute>
<xsl:attribute name="font-size">18pt</xsl:attribute>
</xsl:attribute-set>
<xsl:template match="/">
<html>
<head><title>Ficher personne</title></head>
B.1. ÉLÉMENTS XSLT 259
<body xsl:use-attribute-sets="MonStyle">
Exemple de use-attribut-sets
</body>
</html>
</xsl:template>
</xsl:stylesheet>
xsl:call-template
Syntaxe
p xsl:call-template
name=QName q
p xsl:with-param qts
p /xsl:call-template q
Description
L’élément xsl:call-template permet d’appeler une règle par son nom. Il est utilse dès qu’un frag-
ment doit être instancié à plusieurs endroits d’un programme. Pour éviter des répétitions, on peut placer ce
fragment être placé dans une règle, et appeler cette règle avec xsl:call-template. Le ou les éléments
xsl:with-param peuvent être utilisés pour passer des paramètres à la règle : voir page 282.
La fonctionnalité apportée par cet élémet s’apparente à un appel de fonction dans un langage de pro-
grammation classique. Il faut noter cependant qu’il n’y a pas de valeur retournée, ni de modification
des « arguments » définis par xsl:with-param. Une des manières de contourner ces restrictions est
d’inclure l’appel à xsl:call-template dans un élément xsl:variable : voir page 281.
Exemples
Voir la section consacrée aux règles, page 119.
xsl:choose
Syntaxe
p xsl:choose q
260 APPENDIX B. RÉFÉRENCE XPATH/XSLT
p
xsl:when qvu
p
xsl:otherwise qvw
p /xsl:choose q
Description
Cet élément est associé xsl:when et xsl:otherwise pour créer l’équivalent des structures de test
que l’on trouve habituellement dans les langages de programmation (if-then-else ou switch). Son
contenu est une liste d’éléments xsl:when (au moins un), chacun étant associé à un test, et chacun ayant
un contenu sous forme de corps de règle. Le premier élément xsl:when pour lequel le test s’évalue à
true voit son corps de règle instancié. Les éléments qui suivent sont alors ignorés. Si aucun xsl:when
ne s’évalue à true, le contenu de l’élément xsl:otherwise, s’il existe, est instancié.
Exemples
Le programme suivant affiche tous les titres des chapitres du livre en les classant en trois catégories :
chapitres « courts », « moyens » et « longs ». Le classement s’effectue en fonction de la valeur de l’attribut
LONGUEUR de chaque chapitre.
<xsl:template match="XBOOK">
<CHAPITRES>
<xsl:apply-templates select="CHAPITRE"/>
</CHAPITRES>
</xsl:template>
<xsI:template match="CHAPITRE">
<xsl:choose>
<xsl:when test="@LONGUEUR < 20">
Le chapitre "<xsl:value-of select="TITRE"/>" est court
</xsl:when>
<xsl:otherwise>
Le chapitre "<xsl:value-of select="TITRE"/>"
n’est ni court, ni moyen, ni long !
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
L’attribut test a pour valeur une expression XPath quelconque qui est convertie en booléen selon les
règles présentées dans le chapitre 2, parge 103. Noter que l’opérateur « x » ne peut être utilisé directement
dans le test et doit être remplacé par une référence à l’entité prédéfinie <.
B.1. ÉLÉMENTS XSLT 261
xsl:comment
Syntaxe
x xsl:comment y
corps de règle
x /xsl:comment y
Description
Cette instruction permet d’inclure un commentaire dans le document résultat. Bien entendu il est illusoire
de placer un commentaire sous la forme habituelle x !- ... - y dans le programme XSLT en espérant
le copier dans le document résultat, puisqu’il sera ignoré tout simplement ignoré par le processeur. Le con-
tenu de l’élément peut être un corps de règle avec des instructions XSLT, du texte, des éléments littéraux,
etc. Il faut éviter d’y introduire deux tirets --. L’instanciation du corps de règle sera introduit dans le
document résultat sous la forme :
x !-- instanciation du corps de règle -- y
L’introduction de commentaires peut être utile pour comprendre, en lisant le document résultat, quelles
sont les règles qui ont été instanciées et d’où proviennent les informations.
Exemples
Voici un document représentant (très synthétiquement) le présent livre.
<XBOOK ISBN="1-67098-09">
<TITRE>Publication web avec XML/XSLT</TITRE>
<EDITEUR>Editions O’Reilly France</EDITEUR>
<AUTEURS>
<AUTEUR><NOM>Bernd Amann</NOM>
<AFFILIATION>Cnam</AFFILIATION>
</AUTEUR>
<AUTEUR><NOM>Philippe Rigaux</NOM>
<AFFILIATION>Université Paris-Sud</AFFILIATION>
</AUTEUR>
</AUTEURS>
<CHAPITRE LONGUEUR="8">
<TITRE>Avant-propos</TITRE>
</CHAPITRE>
<CHAPITRE LONGUEUR="43">
<TITRE>Introduction à XML/XSLT</TITRE>
</CHAPITRE>
<CHAPITRE LONGUEUR="34">
<TITRE>Documents XML : Structure et navigation</TITRE>
<MOT-CLE>DOM</MOT-CLE><MOT-CLE>XPath</MOT-CLE>
</CHAPITRE>
<CHAPITRE LONGUEUR="45">
<TITRE>Programmation XSLT</TITRE>
<MOT-CLE>XSLT</MOT-CLE>
</CHAPITRE>
</XBOOK>
262 APPENDIX B. RÉFÉRENCE XPATH/XSLT
Le programme suivant produit la liste de tous les mots-clés présents dans XBook.xml, avec un commen-
taire indiquant de quels chapitres proviennent les mots-clés.
Exemple 117 Comment.xsl : Exemple d’utilisation de xsl:comment
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="XBOOK">
<MOTS-CLES>
<xsl:apply-templates select="CHAPITRE/MOT-CLE"/>
</MOTS-CLES>
</xsl:template>
<xsl:template match="MOT-CLE">
<xsl:comment>
Ces mots-clés sont issus du chapitre
<xsl:value-of select="../TITRE"/>
</xsl:comment>
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
xsl:copy
Syntaxe
x xsl:copy
use-attribute-sets=QNames y
corps de règle
x /xsl:copy y
Description
Cette instruction copie le nœud courant dans le document résultat. Cette copie comprend le balisage et la
valeur, mais n’effectue pas de copie en profondeur. Les éléments sont donc copiés sans leur contenu.
L’attribut use-attribute-set permet d’introduire des attributs dans l’élément copié en faisant
référence au nom d’un groupe d’attributs : voir page 259.
On peut introduire un contenu dans un élément copié en plaçant un corps de règle dans xsl:copy.
Exemples
Voici un document XML :
Exemple 118 Copy.xml : Un document XML à copier
<?xml version="1.0" encoding="ISO-8859-1"?>
<A att1="2">
<!-- Test de xsl:copy -->
<B>Contenu B</B>
<C>Contenu C</C>
</A>
B.1. ÉLÉMENTS XSLT 263
Et voici un programme qui sélectionne dans ce document les principaux types de nœud (à l’exception
de Document et ProcessingInstruction) et les copie :
Exemple 119 Copy.xsl : Un programme d’analyse et de copie
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="ISO-8859-1"/>
<xsl:template match="/">
<xsl:apply-templates select="//node()|@*"/>
</xsl:template>
<xsl:template match="*">
Voici un élément : "<xsl:copy/>"
<xsl:apply-templates select="@*"/>
</xsl:template>
<xsl:template match="@*">
<attribut><xsl:copy/></attribut>
</xsl:template>
<xsl:template match="text()">
Voici du texte : "<xsl:copy/>"
</xsl:template>
<xsl:template match="comment()">
Voici un commentaire : "<xsl:copy/>"
</xsl:template>
</xsl:stylesheet>
1. les attributs sont copiés dans l’élément courant : ici on a introduit un élément littéral x attribut y
dans la règle qui traite les attributs, ce qui implique
(a) que c’est à cet élément que viennent se rattacher les attributs ;
(b) que les éléments issus du document pprincipal se retrouvent sans attribut.
2. le document contient des nœuds de type Text constitués uniquement d’espaces : ces nœuds apparais-
sent ici ;
264 APPENDIX B. RÉFÉRENCE XPATH/XSLT
Une utilisation de courante de xsl:copy consiste à recopier un document en excluant certains élé-
ments ou certains types de nœuds. Le programme suivant par exemple recopie le document Copy.xml en
supprimant les commentaires. Il illustre l’introduction d’un corps de règle dans le contenu de xsl:copy.
<xsl:template match="/">
<xsl:apply-templates select="*|@*|text()"/>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="*|@*|text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="/">
<xsl:apply-templates select="@*|node()"/>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="*|@*|comment()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
B.1. ÉLÉMENTS XSLT 265
xsl:copy-of
Syntaxe
x xsl:copy
select=Expression y
x /xsl:copy-of y
Description
L’élément xsl:copy-of déclenche une copie en profondeur de tous les nœuds sélectionnés par l’expression
de l’attribut select. La copie en profondeur implique que tous les descendants sont copiés également.
Ce recouvre notamment, pour les éléments, la copie de tous les éléments descendants, avec leurs attributs
(qui sont aussi des nœuds) et leur contenu textuel.
Exemple
Le programme suivant copie tout d’abord l’élément racine A, puis recopie en profondeur les fils de type B.
Tous les autres éléments sont ignorés.
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="A">
<xsl:copy/>
<xsl:copy-of select="B"/>
</xsl:template>
</xsl:stylesheet>
xsl:decimal-format
Syntaxe
x xsl:decimal-format
name=nom
decimal-separator=séparateurDécimale
grouping-separator=séparateurGroupe
infinity=infini
minus-sign=signeMoins
NaN=symboleNaN
percent=symbolePourCent
266 APPENDIX B. RÉFÉRENCE XPATH/XSLT
per-mille=symbolePourMille
zero-digit=complémentZéro
digit=complémentDigit
pattern-separator=séparateurPlusMoins y
x /xsl:decimal-format y
Description
blabla
xsl:document
Syntaxe
x xsl:document
href=URI
method= ("xml" | "html" | "text" | autre)
version=noVersion
encoding=codage
omit-xml-declaration=("yes" | "no")
standalone=("yes" | "no")
doctype-public=DTDPublique
doctype-system=DTDSystème
cdata-section-elements=listeEléments
standalone=("yes" | "no")
media-type=typeMedia y
x /xsl:document y
Description
XSLT 1.1
xsl:element
Syntaxe
x xsl:element
name={QName}
namespace={URI}
use-attribute-sets=QNames y
corps de règle
x /xsl:element y
Description
Cette instruction permet d’insérer un élément dans le document résultat. Son principal intérêt est de per-
mettre de déterminer dynamiquement (au moment de l’exécution du programme) le nom ou l’espace de
nom de l’élément créé, tous les autres cas pouvant se ramener à l’insertion d’un élément littéral.
L’attribut use-attribute-sets permet de faire référence à un groupe d’attribut nommé par xsl:attribute-
set : voir page 259.
B.1. ÉLÉMENTS XSLT 267
Exemples
Le programme suivant transforme l’attribut ISBN du document XBook.xml en élément, et copie en pro-
fondeur les éléments x TITRE y et x EDITEUR y .
<xsl:template match="/XBOOK">
<LIVRE>
<xsl:copy-of select="TITRE"/>
<xsl:element name="ISBN">
<xsl:value-of select="@ISBN"/>
</xsl:element>
<xsl:copy-of select="EDITEUR"/>
</LIVRE>
</xsl:template>
</xsl:stylesheet>
Évidemment on pourrait tout aussi bien inclure x ISBN y comme un élément littéral.
xsl:fallback
Syntaxe
x xsl:fallback y
corps de règle
x /xsl:fallback y
Description
Cette instruction s’exécute quand le père de l’élément n’est pas reconnu par le processeur XSLT. Il est donc
principalement destiné à permettre des extensions de XSLT tout en préservant dans une certaine mesure la
portabilité des programmes.
Exemples
Voici un élément de type xbook:refChapitre. Si cet élément est référencé dans un appel de règle, et
que le processeur XSLT est dans l’incapacité de le traiter, le message Instruction inconnue sera
inséré dans le résultat.
x xbook:refChapitre no=’1’ y
x fallback y Instruction inconnue
x /fallback y
x /xbook:refChapitre y
xsl:for-each
Syntaxe
x xsl:for-each
select=Expression y
268 APPENDIX B. RÉFÉRENCE XPATH/XSLT
Description
L’instruction xsl:for-each instancie son contenu pour chacun de nœuds désigné par l’expression de
l’attribut select. Le nœud est pris à chaque fois comme nœud courant, et le contexte pour l’ensemble
de la boucle est constitué de l’ensemble des nœuds sélectionnés par le select. L’instruction xsl:for-
each change donc temporairement le contexte et le nœud courant d’une règle.
Exemples
Voir la description et des cas d’utilisation de cette instruction dans le chapitre 3, page 136.
xsl:if
Syntaxe
x xsl:if
test=Expression y
corps de règle
x /xsl:if y
Description
Cette instruction instancie son contenu si l’expression de l’attribut test s’évalue à true. Il n’y a pas
d’équivalent en XSLT au else habituellement trouvé dans la syntaxe des langages programmation, mais
on peut utiliser l’instruction xsl:choose qui est beaucoup plus puissante : voir page B.1.
Exemples
Voir la description et des cas d’utilisation de cette instruction dans le chapitre 3, page 134.
xsl:import
Syntaxe
x xsl:import href=URI/ y
Description
Cet élément de premier niveau importe un autre programme XSLT avec tous ses éléments de premier
niveau et toutes ses règles. La précédence de tous les éléments importés est inférieure à ceux du document
principal.
Cet élément doit apparaître avant tous les autres éléments de premier niveau parmi les enfants de
l’élément racine xsl:stylesheet.
Nous renvoyons au chapitre 3, page 111 pour une description détaillée des règles d’importation de
documents.
B.1. ÉLÉMENTS XSLT 269
xsl:include
Syntaxe
x xsl:include href=URI/ y
Description
xsl:include insère dans le programme le contenu du document XSLT référencé par l’attribut href.
Tout se passe ensuite comme si les éléments importés avaient été présent dans le programme principal dès
l’origine. Contrairement à xsl:import, il n’existe donc pas d’ordre de précédence entre les éléments
inclus et les autres.
xsl:key
Syntaxe
x xsl:key
name=QName
match=pattern
use=expression/ y
Description
XSLT permet de définir (attribut match) des groupes de nœuds dans le document source, de référencer ces
groupes par un nom (attribut name), et enfin d’indiquer une expression dont le résultat identifie de manière
unique chaque nœud du groupe (attribut use). Tous ces attributs sont obligatoires.
Un fois un groupe (une « clé » dans la terminologie XSLT) défini, on peut extraire un nœud avec la
fonction key().
Exemples
Voic un document XML contenant quelques films avec leurs metteurs en scène.
<FILMS>
<FILM ANNEE="1979">
<TITRE>Alien</TITRE><AUTEUR>Scott</AUTEUR>
</FILM>
<FILM ANNEE="1958">
<TITRE>Vertigo</TITRE><AUTEUR>Hitchcock</AUTEUR>
</FILM>
<FILM ANNEE="1960">
<TITRE>Psychose</TITRE><AUTEUR>Hitchcock</AUTEUR>
</FILM>
<FILM ANNEE="1980">
<TITRE>Kagemusha</TITRE><AUTEUR>Kurosawa</AUTEUR>
</FILM>
<FILM ANNEE="1997">
<TITRE>Volte-Face</TITRE><AUTEUR>Woo</AUTEUR>
</FILM>
<FILM ANNEE="1997">
270 APPENDIX B. RÉFÉRENCE XPATH/XSLT
<TITRE>Titanic</TITRE><AUTEUR>Cameron</AUTEUR>
</FILM>
<FILM ANNEE="1986">
<TITRE>Sacrifice</TITRE><AUTEUR>Tarkovski</AUTEUR>
</FILM>
</FILMS>
Le programme XSLT suivant définit une clé de nom FilmsDate qui définit un groupe contenant tous
les films dont la clé est l’attribut ANNEE de l’élément x FILM y .
<xsl:template match="/">
<xsl:value-of
select=’key("FilmsDate", "1958")/TITRE’/>
</xsl:template>
</xsl:stylesheet>
On peut alors utiliser la fonction key() pour désigner un ou plusieurs des éléments du groupe en fonction
d’une valeur de la clé. Le programme ci-dessus désigne par exemple tous les films de 1958 (soit Vertigo).
On peut noter que key()] s’intégère à des expressions XPath complexes puisque son évaluation donne un
ensemble de nœuds à partir desquels on peut exprimer d’autres étapes XPath.
xsl:message
Syntaxe
x xsl:message
terminate=("yes" | "no") y
corps de règle
x /xsl:message y
Description
Cette instruction permet d’afficher un message sur la « console » du processeur XSLT. Elle peut principale-
ment être utilisée pour suivre le déroulement de l’exécution du programme. L’attribut terminate avec
la valeur yes permet de forcer l’interruption du programme.
xsl:namespace-alias
Syntaxe
x xsl:namespace-alias
stylesheet-prefix=préfixe | "#default"
result-prefix=préfixe | "#default" / y
B.1. ÉLÉMENTS XSLT 271
Description
Cet élément demande au processeur XSLT de modifier le préfixe de certains éléments littéraux quand ils
sont recopiés dans le document résultat. Le préfixe présent dans le programme est indiqué par stylesheet-
prefix, et son équivalent dans le document résultat est donné par result-prefix. Ces deux attributs
sont obligatoires.
Cette transformation est surtout importante pour transformer des documents XSLT eux-mêmes.
Exemples
Supposons que le but d’un programme XSLT soit de produire un autre programme XSLT. On trouverait
alors typiquement des règles comme celles du programme ci-dessous, qui sont évidemment invalides
puisque la présence d’un xsl:stylesheet n’est pas autorisée dans un corps de règle.
Exemple 128 NamespaceAlias1.xsl : Problème d’un élément littéral avec un préfixe xsl:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:stylesheet version="1.0">
<xsl:apply-templates />
</xsl:stylesheet>
</xsl:template>
</xsl:stylesheet>
La solution consiste à utiliser, dans le programme, un préfixe spécial, par exemple myxsl, pour les
éléments littéraux, et à convertir ce préfixe spécial en xsl au moment de l’inclusion de ces éléments
littéraux dans le document résultat. La conversion est indiquée avec xsl:NamspaceAlias, comme
dans l’exemple ci-dessous.
<xsl:namespace-alias
stylesheet-prefix="myxsl"
result-prefix="xsl"/>
<xsl:template match="/">
<myxsl:stylesheet version="1.0">
<xsl:apply-templates/>
</myxsl:stylesheet>
</xsl:template>
</xsl:stylesheet>
xsl:number
Syntaxe
x xsl:number
272 APPENDIX B. RÉFÉRENCE XPATH/XSLT
Description
Blabla
xsl:otherwise
Syntaxe
x xsl:otherwise y
corps de règle
x /xsl:otherwise y
Description
Cet élément est subordonné à l’élément xsl:choose : voir page 261.
xsl:output
Syntaxe
x xsl:output
method="xml" | "html" | "text" | QName
version=Ntoken
encoding=string
omit-xml-declaration=("yes" | "no")
standalone=("yes" | "no")
doctype-public=string
doctype-system=strin
cdata-section-elements=QNames
indent=("yes" | "no")
media-type=string / y
Description
Cet élément indique au processeur le type de document à produire. En fonction de ce type, des mises
en forme spéciales peuvent être appliquées. Les trois types de document préconisés par la norme sont
html, xml et text, la valeur par défaut étant xml. De plus d’autres types de document spécifiques à un
processeur peuvent être pris en compte, mais le programme ne sera alors pas portable.
Les attributs version et encoding correspondent à la version XML (à l’heure actuelle la 1.0) ou
HTML (à l’heure actuelle la 4.0), et à l’encodage du document résultat.
B.1. ÉLÉMENTS XSLT 273
L’attribut omit-xml-declaration (avec pour valeur par défaut no) peut être utilisé pour sup-
primer la déclaration XML (sa valeur doit alors être yes). L’attribut standalone est spécifique à un
document résultat de type XML : il indique si le document fait référence ou non à des entités externes.
Les attributs doctype-public et doctype-system peuvent être utilisés respectivement pour un
document XML avec DTD publique et un document XML avec une DTD de type SYSTEM.
On peut demander à ce que les fils de type Text de certains éléments dans le document XML résultat
fassent partie d’une section littérale (CDATA). Dans ce cas les types de ces éléments doivent être donnés,
séparés par des espaces, dans la valeur de l’attribut cdata-section-elements.
Le document résultat peut être indenté si l’attribut indent est à yes (ce qui n’est pas le cas par
défaut). Enfin l’attribut media-type donne le type MIME du document produit. Ce type peut être utile
quand le document est transmis par le protocole HTTP. Pour HTML il doit valoir text/html, pour WML
text/xml, etc. Sa valeur par défaut est text/xml.
Exemples
Voir page 197 la description de cet élément et son application à une transformation XHTML –> HTML.
xsl:param
Syntaxe
x xsl:param
name=QName
select=Expression y
corps de règle
x /xsl:param y
Description
1. dans une règle, pour indiquer un paramètre local reconnu et pris en compte par la règle ;
2. comme élément de premier niveau, pour désigner des paramètres extérieurs passés au programme.
L’attribut select est optionnel. S’il est présent, il donne la valeur par défaut du paramètre. Si
select est omis, le corps de l’élément (également optionnel) xsl:param définit la valeur par défaut du
paramètre.
Exemples
xsl:preserve-space
Syntaxe
x xsl:preserve-space
elements=liste-éléments/ y
274 APPENDIX B. RÉFÉRENCE XPATH/XSLT
Description
Cet élément, ainsi que son complément xsl:strip-space, permet de contrôler la prise en compte
des nœuds de type Text constitués uniquement de blancs. Il est associé à une liste d’éléments donnée dans
l’attribut elements. Pour tous les élément indiqués, le nœuds de type domtypeText doivent être préservés
même s’ils ne contiennent que des espaces.
Par défaut le processeur XSLT doit conserver les nœuds de textes vides, ce qui peut introduire des effets
désagréables, notamment quand on utilise la fonction position(). Tout se passe donc comme si l’option
suivante était prise par défaut :
x xsl:preserve-space elements="*"/ y
Exemples
L’exemple suivant est un document avec l’indentation habituelle pour clarifier la hiérarchie des éléments.
x A y et x B y ont tous deux deux fils de type Text vides, encadrant un élément.
Exemple 130 Space.xml : Un document XML avec indentation
<A>
<B>
<C>Contenu</C>
</B>
</A>
Le programme suivant commence par définir une règle inverse à l’option par défaut : tous les nœuds de
type Text constitués uniquement d’espaces doivent être supprimés. On procède ensuite par exception en
indiquant que ce type de nœud doit être préservé uniquement pour le éléments de type B.
<xsl:strip-space elements="*"/>
<xsl:preserve-space elements="B"/>
<xsl:template match="/A">
Fils de A : <xsl:value-of select="count(node())"/>
<xsl:apply-templates select="B"/>
</xsl:template>
<xsl:template match="B">
Fils de B : <xsl:value-of select="count(node())"/>
</xsl:template>
</xsl:stylesheet>
On appelle ensuite deux règles, l’une pour les éléments de type A, l’autre pour les éléments de type B,
et dans chaque règle on affiche le nombre de fils du nœud courant. On obtient 1 pour x A y et 3 pour x B y .
B.1. ÉLÉMENTS XSLT 275
xsl:processing-instruction
Syntaxe
x xsl:processing-intruction
name=QName y
corps de règle
x /xsl:processing-instruction y
Description
Cette instruction permet d’insérer un nœud de type ProcessingInstruction dans le document résultat.
L’attribut name est obligatoire et donne le nom ou « cible » de l’instruction de traitement. Le contenu de
l’élément xsl:processing-instruction doit produire du texte formant le contenu de l’instruction
de traitement dans le document résultat
xsl:sort
Syntaxe
x xsl:sort
select=expression
lang=codeLangue
order=("ascending" | "descending")
case=("lower-first" | "upper-first")
data-type= ("text" | "number" ) / y
Description
Cet élément est utilisé pour trier les nœuds séléctionnés soit par un appel de règles xsl:apply-templates,
soit par une boucle xsl:for-each. L’élément xsl:sort doit toujours apparaître immédiatement
après la balise ouvrante de xsl:for-each ou xsl:apply-templates, le mélange avec des élé-
ments xsl:with-param étant possible dans le second cas.
La clé de tri est indiquée par l’expression de l’attribut select (tous les attributs sont optionnels). Cette
expression est évaluée en prenant pour nœud courant, tour à tour, chaque nœud de l’ensemble sélectionné
par xsl:for-each ou xsl:apply-templates. Le tri de l’ensemble s’effectue alors d’apprès les
valeurs obtenues.
Par défaut le tri se fait d’après l’ordre lexicographique sur les chaînes de caractères, ce qui signifie que
« 4 » est plus grand que « 30 » (les caractères sont comparés de gauche à droite). En indiquant la valeur
number pour l’attribut data-type, on obtient un classement numérique.
L’ordre de tri est croissant par défaut et peut être modifié par l’attribut order. L’attribut lang est
supposé indiqué l’ordre des caractères spéciaux (est-ce que « é » est inférieur ou supérieur à « è » ?) qui
dépend de la langue. Enfin case indique le classement des mots en majuscule par rapport aux mêmes
mots en minuscules.
On peut indiquer plusieurs xsl:sort successifs : le processeur effectue un premier tri avec la pre-
mière clé, puis un second tri sur les groupes qui n’ont pas été départagés par le premier tri, et ainsi de
suite.
Exemples
Le tri est présenté page 140. Voici un exemple complémentaire montrant comment trier les chapitres du
document XBook.xml (page 261) d’après leur nombre de pages.
<xsl:template match="/">
<CHAPITRES-TRIES>
<xsl:apply-templates select="//CHAPITRE">
<xsl:sort select="@LONGUEUR" data-type="number"/>
</xsl:apply-templates>
</CHAPITRES-TRIES>
</xsl:template>
<xsl:template match="CHAPITRE">
<CHAPITRE>
<TITRE><xsl:value-of select="TITRE"/></TITRE>
<LONGUEUR><xsl:value-of select="@LONGUEUR"/></LONGUEUR>
</CHAPITRE>
</xsl:template>
</xsl:stylesheet>
Remarquez l’attribut data-type avec la valeur number. Par défaut, le classement lexicographique
dirait que l’avant-propos (8 pages) est plus long que tous les autres chapitres.
Toute valeur associée à un nœud par une expression XPath peut être utilisée comme critère de tri.
Reprenons le document Films.xml, page 269, et supposons que le fichier Artiste.xml contienne les infor-
martions sur les metteurs en scènes.
<ARTISTES>
<ARTISTE><NOM>Scott</NOM>
<PRENOM>Ridley</PRENOM>
<ANNEE>1943</ANNEE>
</ARTISTE>
<ARTISTE><NOM>Hitchcock</NOM>
<PRENOM>Alfred</PRENOM>
<ANNEE>1899</ANNEE>
</ARTISTE>
<ARTISTE><NOM>Kurosawa</NOM>
<PRENOM>Akira</PRENOM>
<ANNEE>1910</ANNEE>
</ARTISTE>
<ARTISTE><NOM>Woo</NOM>
<PRENOM>John</PRENOM>
<ANNEE>1946</ANNEE>
</ARTISTE>
<ARTISTE><NOM>Cameron</NOM>
<PRENOM>James</PRENOM>
<ANNEE>1954</ANNEE>
</ARTISTE>
<ARTISTE><NOM>Tarkovski</NOM>
<PRENOM>Andrei</PRENOM>
<ANNEE>1932</ANNEE>
</ARTISTE>
</ARTISTES>
B.1. ÉLÉMENTS XSLT 277
Voici un programme qui trie les films en fonction de l’année de naissance de leur metteur en scène.
<xsl:variable name="artistes"
select="document(’Artistes.xml’)/ARTISTES"/>
<xsl:template match="FILMS">
<FILMS-TRIES>
<xsl:apply-templates select="FILM">
<xsl:sort
select="$artistes/ARTISTE[NOM = current()/AUTEUR]/ANNEE"
data-type="number"/>
</xsl:apply-templates>
</FILMS-TRIES>
</xsl:template>
<xsl:template match="FILM">
<FILM>
<TITRE><xsl:value-of select="TITRE"/></TITRE>
<AUTEUR><xsl:value-of select="AUTEUR"/></AUTEUR>
<NE-EN>
<xsl:value-of
select="$artistes/ARTISTE[NOM = current()/AUTEUR]/ANNEE"/>
</NE-EN>
</FILM>
</xsl:template>
</xsl:stylesheet>
La variable $artistes est initialisée avec le contenu du document Artistes.xml (voir la description
de la fonction document() page 283). À partir de là on peut l’utiliser comme s’il s’agissait de l’élément
racine de ce document. L’expression :
$artistes/ARTISTE[NOM = current()/AUTEUR]/ANNEE
sélectionne l’élément x ARTISTE y dont l’élément fils x NOM y a un contenu identique à celui du nœud
courant. Attention : il s’agit de l’un des rares cas où le nœud contexte de XPath est différent du nœud
courant de XSLT.
xsl:strip-space
Syntaxe
x xsl:strip-space
278 APPENDIX B. RÉFÉRENCE XPATH/XSLT
elements=QNames/ y
Description
Cet élément donne la liste des éléments du documents source dont les fils de type Text constitués unique-
ment d’espaces doivent être supprimés. L’attribut elements est obligatoire : il indique, séparés par des
blancs, la liste des éléments concernés par xsl:strip-space. On peut utiliser le caractère « * » pour
désigner tous les éléments.
Exemples
Voir page 82, ainsi que la description de xsl:preserve-space, page 274.
xsl:stylesheet
Syntaxe
x xsl:stylesheet
id=idElément
extension-elements-prefixes=tokens
exclude-result-prefixes=tokens
version=number y
éléments de premier niveau
x /xsl:stylesheet y
Description
Cet élément est toujours l’élément racine d’un programme XSLT (xsl:transform est un synonyme).
Il doit toujours être accompagné d’un numéro de version (actuellement seule la 1.0 est une recommanda-
tion du W3C) afin de permettre au processeur de s’adapter aux éventuelles différences entre les versions
successives de XSLT. Tous les autres attributs sont optionnels.
L’attribut id donne un identifiant au programme. Cet attribut n’est utile que quand le programme est
inclus directement dans un document XML, et permet alors d’identifier le nœud racine des instructions
XSLT.
L’attribut extension-elements-prefixes donne la liste des préfixes des éléments qui sont
des extensions de XSLT. On peut exclure les éléments de certains espaces de nom en les indiquant dans
l’attribut exclude-result-prefixes.
Les fils de xsl:stylesheet sont tous les éléments de premier niveau de XSLT (voir tableau 3.1,
page 112, les éléments de type xsl:import devant apparaître en premier.
Exemples
Voir notamment la section consacrée à la structure d’un document XSLT, page 111.
xsl:template
Syntaxe
x xsl:template
name=QName
match=Pattern
mode=QName
priority=number y
B.1. ÉLÉMENTS XSLT 279
Description
Cet élément définit une règle XSLT. Une règle peut être appelée par son nom (attribut name) avec une
instruction xsl:call-template. On peut aussi l’associer à un pattern avec l’attribut match, et la
règle est déclenchée pour tous les nœuds auxquels le pattern s’applique. L’un des deux attributs doit être
défini, mais pas les deux à la fois.
Deux attributs complémentaires conditionnent le choix d’une règle. L’attribut priority détermine
le degré de priorité : il est pris en compte quand plusieurs règles sont possibles pour un xsl:apply-
templates. L’attribut mode permet de définir plusieurs catégories de règles à appliquer dans des con-
textes différents.
Exemple
Voir la section du chapitre 3 consacrée aux règles, page 119.
xsl:text
Syntaxe
x xsl:text
disable-output-escaping=("yes" | "no") y
#PCDATA
x /xsl:text y
Description
Cette instruction permet d’insérer un nœud de type Text dans le document résultat. Le contenu de l’élément
ne doit pas contenir de balisage : les caractères spéciaux doivent donc être référencés via une entité
prédéfinie comme < pour x ou & pour &. Par défaut tous ces caractères spéciaux seront à
leur tour produits sous forme de référence à un entité dans le document résultat, mécanisme désigné par le
terme escaping en anglais.
Pour insérer directement le caractère, et pas la référence à l’entité, on peut mettre à no l’attribut
disable-output-escaping. Attention : cela affecte le document résultat, et pas le programme
XSLT qui doit, lui, toujours utiliser les références aux entités.
Les blancs introduits dans le contenu d’un élément xsl:text sont toujours préservés dans le docu-
ment résultat.
Exemples
Le programme XSLT ci-dessous produit une simple phrase contenant quelques caractères réservés. L’utilisation
des entités est requise.
<xsl:template match="/">
<xsl:text disable-output-escaping="yes">
Si 0 < 1, alors 1 > 0,
& vice et versa
280 APPENDIX B. RÉFÉRENCE XPATH/XSLT
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Le résultat de ce programme est le suivant. Il contient des caractères réservés qui le rendent impropre
à une inclusion dans un document XML bien formé.
Si 0 x 1, alors 1 y 0,
& vice et versa
Si disable-output-escaping n’était pas défini (ou défini à no, ce qui revient au même), on
obtiendrait dans le résultat une version avec entités identique à celle du programme XSLT.
xsl:transform
Syntaxe
Voir xsl:stylesheet
Description
Cet élément est synonyme de xsl:stylesheet : voir page 278.
xsl:value-of
Syntaxe
x xsl:value-of
select=Expression
disable-output-escaping=("yes" | "no") / y
Description
Cet élément évalue une expression sur le document source (ou sur un document référencé par la fonction
document()), puis convertit le résultat en chaîne de caractères selon les règles de conversion présentées
dans le chapitre 2, et enfin insère la chaîne dans le document résultat.
L’attribut disable-output-escaping a la même signification que dans le cas de l’élément xsl:text
décrit précédemment.
Exemples
Voir la section consacrée à cet élément, page ??.
xsl:variable
Syntaxe
x xsl:variable
name=QName
select=Expression / y
corps de règle
x /xsl:variable y
B.1. ÉLÉMENTS XSLT 281
Description
Une variable dans XSLT est un nom référençant une information qui peut être de nature très variée : un
nœud, un ensemble de nœuds, une valeur numérique ou alphanumérique, ou un result tree fragment, type
spécifique à XSLT 1.0 qui va probablement disparaître avec XSLT 1.1.
La valeur d’une variable est définie soit par l’évaluation de l’expression associée à l’attribut select
(auquel cas le contenu doit être vide), soit par le contenu de l’élément xsl:variable s’il n’y a pas de
select.
La portée d’une variable dépend de la position de l’élément. Si c’est un élément de premier niveau
(fils de xsl:stylesheet) la variable est définie dans tout le programme XSLT. Si, en revanche, la
variable est définie dans un élément xsl:template, elle est visible dans tous ses frères droits et leurs
descendants.
On ne peut pas définir une variable avec le même nom qu’une variable visible. (autrement dit on ne
peut pas trouver deux xsl:variable avec la même valeur pour l’attribut name si les éléments ont, au
moins en partie, le même espace de visibilité). Il s’ensuite qu’une variable est plutôt une constante, au sens
habituellement donné à ce mot en programmation. Cette particularité fait partie des propriétés qui font de
XSLT un langage « déclaratif », et laisse un certain degré de liberté au processeur XSLT pour évaluer un
programme.
Cette interdiction ne s’applique pas aux définitions de variables présentes dans des documents im-
portés : dans ce cas l’ordre de précédence joue et la définition de variable présente dans le document
principal ou dans le document importé ayant la plus grande précédence est prise en compte.
Exemples
Voir la section consacrée aux variables, page 138.
xsl:when
Syntaxe
x xsl:value-of
test=Expression / y
corps de règle
x /xsl:variable y
Description
Cet élément est subordonné à l’élément xsl:choose : voir page 261.
xsl:with-param
Syntaxe
x xsl:value-of
name=QName
select=Expression / y
corps de règle
x /xsl:with-param y
Description
Cet élément est utilisé pour passer des paramètres à une règle. Il suit un xsl:call-template, un
xsl:apply-templates ou un xsl:apply-imports.
282 APPENDIX B. RÉFÉRENCE XPATH/XSLT
La valeur du paramètre est déterminée comme pour les variables, en évaluant l’attribut select s’il est
présent, ou en prenant le contenu de l’élément sinon. Comme beaucoup d’autres aspects de la programma-
tion XSLTY, le passage de paramètres est très faiblement typé : on peut passer à une règle des paramètres
qu’elle n’attend pas, ou au contraire ne pas lui passer de paramètres. Dans le premier cas le paramètre est
tout simplement ignoré. Dns le second cas la règle prend en compte la valeur par défaut du paramètre, si
cette valeur existe : voir page B.1.
Exemples
Voir la section consacrée au passage de paramètres, page 132.
boolean
ceiling
concat
contains
Cette fonction teste si chaîne contient sous-chaîne, et renvoie true ou false selon le cas.
count
current
node-set current ( )
Cette fonction renvoie le nœud courant ou, pour être plus précis, node-set contenant un seul nœud, le
nœud courant.
Qu’est-ce que le nœud courant (qu’il faut bien distinguer du nœud contexte pour une expression XPath).
Un programme XSLT consiste à considérer certains nœuds du document source, à rechercher quelle est la
règle associée à chaque nœud et à instancier cette règle. Chaque règle s’instancie donc en association avec
le nœud qui a déclenché sont instanciation : c’est le nœud courant.
Au sein d’une règle on peut trouver des instructions qui vont temporairement changer le nœud courant :
xsl:apply-templates va sélectionner, avec une expression XPath un ensemble de nœud et les pren-
dre chacun à son tour comme nœuds courants pour instancier un règle, et de même pour xsl:for-each.
Ces deux éléments modifient localement le nœud courant, qui revient à sa valeur initiale quand l’évaluation
de xsl:apply-templates ou xsl:for-each est terminée.
Le nœud courant peut être différent du nœud contexte, désigné dans XPath par l’expression self:node()
ou « . ». Par exemple, dans l’expression suivante :
FILM/TITRE[.=’Alien’]
le nœud courant est celui à partir duquel on va chercher un fils de type FILM, puis un petit-fils de type
TITRE, etc. Dans cette expression en revanche, le nœud contexte désigné par « . » est un élément de type
TITRE. Les prédicats sont les seuls cas où le nœud courant et le nœud contexte diffèrent, et les seuls cas
où l’utilisation de la fonction current() est justifiée. Voici par exemple un appel de règle qui sélectionne
tous les nœuds frères du nœud courant qui ont le même contenu.
x xsl:apply-templates match="../*[.=current()]" y
La réféence de l’élément xsl:sort présente un exemple où l’utilisation de current est impérative.
document
TO DO
element-available
Cette fonction teste si un élément XSLT est connu du processeur. Elle est principalement utile pour tester
si certaines extensions largement répandues (comme, par exemple, xsl:document) sont disponibles.
284 APPENDIX B. RÉFÉRENCE XPATH/XSLT
false
Boolean false ()
Cette fonction renvoie false, et peut être utilisée pour palier l’absence de constantes booléennes dans
XSLT. En pratique les conversions effectuées automatiquement sont le plus souvent suffisantes, et évitent
d’avoir à écrire « expression!=false() » alors que « expression » a une signification équiva-
lente.
floor
Cette fonction renvoie le plus grand entier immédiatement inférieur à valeur. Par exemple floor(1.5)
est 1. Appliquée à une valeur entiére, la fonction renvoie cette valeur.
format-number
string format-number ()
TO DO
function-available
Cette fonction teste si un élément XSLT est connu du processeur. Elle peut être utile pour vérifier l’existence
de fonctions fournies seulement par certains processeurs, ou apparues dans de nouvelles versions de XSLT.
generate-id
TO DO
id
TO DO
key
La fonction key() fonctionne en association avec l’élément xsl:key décrit page 270. Rappelons que
cet élément définit un groupe de nœud, lui attribue un nom et définit l’expression (la clé) qui permet de
différencier les nœuds au sein de ce groupe.
La fonction prend deux arguments : le nom de la clé, et la valeur de la clé. Elle renvoie le nœud identifié
par cette valeur.
B.2. FONCTIONS XPATH/XSLT 285
Exemples
Voir page 270.
lang
Cette fonction vérifie que le langage du nœud contexte est codeLangue, et renvoie true ou false
selon le cas.
Le langage d’un élément est défini par l’attribut réservé xml:lang, soit dans l’élément lui-même, soit
dans le plus proche ancêtre ayant cet attribut.
last
number last ()
La fonction last() renvoie la taille du contexte XPath. Dans XSLT il s’agit du nombre de nœud sélection-
nés par l’expression de l’attribut select dans xsl:apply-templates ou xsl:for-each. Si la
fonction est utilisée dans une prédicat XPath, elle correspond au nombre de nœuds sélectionnés par l’étape
précédente.
Il faut être attentif aux nœuds textes vides qui peuvent être comptés ou non selon que l’instruction
xsl:strip-space a été utilisée ou pas.
local-name
La fonction renvoie le nom local (sans le préfixe correspondant à l’espace de nom) du nœud passé en
argument. Si l’argument est un ensemble, le premier nœud est pris en compte. Si l’argument est omis la
fonction s’applique au nœud contexte.
name
namespace-uri
normalize-space
TO DO
286 APPENDIX B. RÉFÉRENCE XPATH/XSLT
not
number
position
number position ()
Cette fonction renvoie la position de l’objet courant dans l’ensemble de nœud que constitue le contexte
XPath.
round
starts-with
string
TO DO
string-length
TO DO
substring
TO DO
B.2. FONCTIONS XPATH/XSLT 287
substring-after
TO DO
substring-before
TO DO
sum
Cette fonction effectue la somme des valeurs des nœuds dans le node-set passé en argument.
system-property
TO DO
translate
TO DO
true
Boolean true ()
Cette fonction renvoie true, et peut être utilisée pour palier l’absence de constantes booléennes dans
XSLT. En pratique les conversions effectuées automatiquement sont le plus souvent suffisantes, et évitent
d’avoir à écrire « expression=true() » alors que « expression » a une signification équivalente.
unparsed-entity-uri
TO DO