P. 1
Comprendre XSLT

Comprendre XSLT

|Views: 571|Likes:
Publié parSamanis Bat

More info:

Published by: Samanis Bat on Jan 02, 2011
Droits d'auteur :Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

11/27/2011

pdf

text

original

Sections

Publication web avec XML/XSLT

Bernd Amann et Philippe Rigaux August 6, 2001

2

Contents
1 Introduction à XML et XSLT 1.1 L’application et ses besoins . . . . . . . . . . . . . . . . 1.2 XML, format universel . . . . . . . . . . . . . . . . . . 1.2.1 Qu’est-ce que XML ? . . . . . . . . . . . . . . . 1.2.2 Structuration avec XML . . . . . . . . . . . . . 1.2.3 Documents XML . . . . . . . . . . . . . . . . . 1.3 Publication de données avec XSLT . . . . . . . . . . . . 1.3.1 Site web (HTML) . . . . . . . . . . . . . . . . . 1.3.2 Site WAP (WML) . . . . . . . . . . . . . . . . 1.3.3 Document papier (PDF) . . . . . . . . . . . . . 1.4 Échange et intégration de données en XML . . . . . . . 1.4.1 Exemple : Le Site www.sallesenligne.com . . . . 1.4.2 Description de la structure d’un document XML 1.4.3 Transformation et échange de données . . . . . . 1.4.4 Un moteur de recherche XML/XSLT . . . . . . 1.4.5 Intégration dynamique de fragments XML . . . . 1.5 Comment lire la suite de ce livre ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 14 14 15 15 18 21 21 31 38 42 42 43 44 45 46 51 55 56 57 57 58 58 59 59 60 61 61 62 64 64 66 66 66 71 72 74 74 76 78 79

2

Documents XML : structure et navigation 2.1 La syntaxe XML . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1 Déclaration XML . . . . . . . . . . . . . . . . . . . . 2.1.2 Déclaration du type de document et des entités . . . . 2.1.3 Commentaires . . . . . . . . . . . . . . . . . . . . . 2.1.4 Instructions de traitement . . . . . . . . . . . . . . . . 2.1.5 Éléments . . . . . . . . . . . . . . . . . . . . . . . . 2.1.6 Attributs . . . . . . . . . . . . . . . . . . . . . . . . 2.1.7 Espaces . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.8 Sections CDATA . . . . . . . . . . . . . . . . . . . . 2.1.9 Références d’entités . . . . . . . . . . . . . . . . . . 2.1.10 Balises, données caractères et valeur textuelle . . . . . 2.2 Le modèle DOM . . . . . . . . . . . . . . . . . . . . . . . . 2.2.1 Types de nœuds DOM . . . . . . . . . . . . . . . . . 2.2.2 Interface DOMString . . . . . . . . . . . . . . . . . 2.2.3 Interfaces NodeList et NamedNodeMap . . . . . . . 2.2.4 Interface Node . . . . . . . . . . . . . . . . . . . . . 2.2.5 Interface Document . . . . . . . . . . . . . . . . . . 2.2.6 Interfaces Element et Attr . . . . . . . . . . . . . . . 2.2.7 Interfaces DocumentType, Entity et EntityReference 2.3 Du document sérialisé à l’arbre DOM . . . . . . . . . . . . . 2.3.1 Construction d’un arbre DOM . . . . . . . . . . . . . 2.3.2 Traitement des espaces pendant la construction . . . . 2.3.3 Deux fonctions de navigation . . . . . . . . . . . . . 3

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

4 2.4 Le langage XPath . . . . . . . . . . . . . . . . . . 2.4.1 Représentation XPath d’un document XML 2.4.2 Expressions XPath . . . . . . . . . . . . . 2.4.3 Les axes . . . . . . . . . . . . . . . . . . . 2.4.4 Les filtres . . . . . . . . . . . . . . . . . . 2.4.5 Prédicats . . . . . . . . . . . . . . . . . . 2.4.6 Types et opérations XPath . . . . . . . . . 2.4.7 Exemples d’expressions XPath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CONTENTS
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 . 82 . 86 . 91 . 98 . 100 . 103 . 107 109 110 111 115 117 119 119 122 124 126 131 132 134 134 136 138 140 141 146 153 154 154 154 156 160 164 165 165 166 170 171 173 175 176 177 177 181 187 195 195 197 197 197

3

XSLT 3.1 Programmes XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . 3.1.1 Structure d’un programme XSLT . . . . . . . . . . . . . . 3.1.2 Modularité : xsl:import et xsl:include . . . . . . . 3.1.3 Application d’un programme XSLT . . . . . . . . . . . . . 3.2 Les règles XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.1 Les patterns . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.2 Règles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.3 Déclenchement de règles avec xsl:apply-templates . 3.2.4 Sélection des règles . . . . . . . . . . . . . . . . . . . . . . 3.2.5 Appel de règle avec xsl:call-template . . . . . . . . 3.2.6 Paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3 Instructions de contrôle . . . . . . . . . . . . . . . . . . . . . . . . 3.3.1 Tests : xsl:if et xsl:choose . . . . . . . . . . . . . . 3.3.2 Boucles : xsl:for-each . . . . . . . . . . . . . . . . . 3.3.3 Variables : xsl:variable . . . . . . . . . . . . . . . . . 3.3.4 Tri : xsl:sort . . . . . . . . . . . . . . . . . . . . . . . 3.3.5 Itérations par récursion . . . . . . . . . . . . . . . . . . . . 3.4 Évaluation d’un programme XSLT . . . . . . . . . . . . . . . . . . Production de documents XML 4.1 Définition de Types de Documents : DTD . . . . . . . . . . . 4.1.1 Pourquoi définir une DTD ? . . . . . . . . . . . . . . 4.1.2 Entités . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.3 Éléments . . . . . . . . . . . . . . . . . . . . . . . . 4.1.4 Attributs . . . . . . . . . . . . . . . . . . . . . . . . 4.1.5 DTD et XML Schéma . . . . . . . . . . . . . . . . . 4.2 Site Web: HTML . . . . . . . . . . . . . . . . . . . . . . . . 4.2.1 HTML et XHTML . . . . . . . . . . . . . . . . . . . 4.2.2 DTD XHTML . . . . . . . . . . . . . . . . . . . . . 4.2.3 Génération de pages HTML: xsl:output . . . . . 4.2.4 Transformation d’une page XML . . . . . . . . . . . 4.2.5 Création de liens . . . . . . . . . . . . . . . . . . . . 4.2.6 XML/XSLT: une solution du problème des liens cassés 4.2.7 Intégration de pages XML . . . . . . . . . . . . . . . 4.3 Présentations multimédia: SMIL . . . . . . . . . . . . . . . . 4.3.1 SMIL par un exemple . . . . . . . . . . . . . . . . . 4.3.2 DTD SMIL . . . . . . . . . . . . . . . . . . . . . . . 4.3.3 Génération de présentations SMIL . . . . . . . . . . . 4.4 Traitement de texte dans XSLT . . . . . . . . . . . . . . . . . 4.4.1 xsl:preserve-space et xsl:strip-space . 4.4.2 Génération de texte : xsl:text . . . . . . . . . . . 4.4.3 Sérialisation du résultat : xsl:output . . . . . . . . 4.5 RSS et RDF . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

4

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . .

CONTENTS
5 6 7 Production de documents papier Échange et intégration Publication de bases de données 7.1 Bases de données et XML . . . . . . . . . . . . . . . . . . . . . . . . 7.1.1 Quelques rappels sur les BD relationnelles . . . . . . . . . . . 7.1.2 Documents orientés « texte » et documents orientés « données » 7.1.3 Transformation d’une base de données en XML . . . . . . . . . 7.1.4 Création de la DTD . . . . . . . . . . . . . . . . . . . . . . . . 7.2 Architectures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2.1 Une classe Java d’exportation XML . . . . . . . . . . . . . . . 7.2.2 Architecture Servlet . . . . . . . . . . . . . . . . . . . . . . . 7.2.3 Utilisation des Java Server Pages . . . . . . . . . . . . . . . . 7.3 XML dynamique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3.1 XSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3.2 XSQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.4 Perspectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Un serveur de publication XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5 199 201 203 204 204 207 209 215 218 218 222 225 230 230 235 239 241 243 244 244 245 245 245 246 247 247 247 248 249 250 251 251 251 252

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

8

A L’environnement XML/Apache A.1 Le projet XML/Apache . . . . . . . . . . . . . . . . . A.2 Xalan . . . . . . . . . . . . . . . . . . . . . . . . . . A.2.1 Préliminaire : le Java Development Kit . . . . A.2.2 Installation de Xalan . . . . . . . . . . . . . . A.2.3 Test d’expressions XPath avec ApplyXPath . . A.2.4 Effectuer des transformations XSLT avec Xalan A.2.5 Utiliser Xalan en Applet ou en Servlet . . . . . A.3 Cocoon . . . . . . . . . . . . . . . . . . . . . . . . . A.3.1 Tomcat . . . . . . . . . . . . . . . . . . . . . A.3.2 Cocoon . . . . . . . . . . . . . . . . . . . . . A.4 Intégration Apache/Tomcat/Cocoon . . . . . . . . . . A.4.1 Compilateur . . . . . . . . . . . . . . . . . . A.4.2 Apache . . . . . . . . . . . . . . . . . . . . . A.4.3 PHP . . . . . . . . . . . . . . . . . . . . . . . A.4.4 Lien Apache/Tomcat . . . . . . . . . . . . . . A.4.5 Et Cocoon ? . . . . . . . . . . . . . . . . . . .

B Référence XPath/XSLT 253 B.1 Éléments XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 B.2 Fonctions XPath/XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282

6

CONTENTS

Avant-propos
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 :

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

       

la volonté de réutiliser un même contenu dans différents contextes ; l’impossibilité de réaliser une application complexe et interactive avec HTML ; 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.

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 restructurer 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 application 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.

Objectifs et contenu de ce livre
Le présent livre est principalement destiné à couvrir de manière complète, progressive et détaillée, le langage XSLT et ses applications à la transformation de documents XML. Cet axe principal est complété par la recherche d’une mise en perspective du pourquoi et du comment de telles transformations. Autrement dit nous avons cherché, autant que les limites fixées par la clarté de la présentation le permettaient, à définir un contexte d’utilisation réaliste pour motiver le recours à XSLT et montrer ses apports. Au cours des différents chapitres, nous envisageons donc différents « cas d’utilisation » de XML. Ce choix nous amène, en pratique, à aller bien plus loin que la simple création de pages HTML, comme le terme de « publication web » pourrait à tort le laisser croire. En particulier nous développons les aspects relatifs aux échanges de données entre deux applications et , avec des transformations XSLT prenant en entrée un document XML reconnu par l’application , et produisant en sortie un autre document XML reconnu par l’application . Ces échanges constituent un aspect essentiel du rôle de XML dans des applications réparties, et XSLT, par sa capacité à transformer automatiquement les informations structurées avec XML, y tient une place importante. Un dernier thème abordé dans ce livre est l’intégration de données au format XML, préalable à une transformation. Le langage XML peut en effet être vu comme un moyen pour unifier dans un format cohérent des informations hétérogènes issues de sources diverses. Nous consacrons en particulier une place importante à la création « à la volée » de documents XML à partir d’une base de données, suivie d’une transformation XSLT. Une telle aproche offre une alternative intéressante à une solution basée sur

¢

¡

¡

¢

CONTENTS

9

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 manipulation 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 2. les principes des bases de données relationnelles (notamment SQL) ; 3. un peu de programmation Java.

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 propose, 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. Documents XML. Présentation de la syntaxe XML (éléments, attributs, liens, entités..) ; 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 :

Les commandes UNIX sont précédées de %. Par exemple : % ls -l De plus, nous adopterons également des conventions précises pour nommer les fichiers, les variables, les fonctions, les noms de tables, etc. Ces conventions font partie d’une stratégie générale de qualité du développement et seront présentées le moment venu.

Remerciements
Irini Fundulaki, Cédric Dumouza, Laurent Mignet, Michel Scholl, Luc Ségoufin, Dan Vodislav

     

La police à chasse constante s’applique à tous les exemples de code, de commande et de programme. La police à chasse constante en italiques est utilisée pour distinguer les paramètres des mot-clé dans la syntaxe des commandes. Le texte en italiques est utilisé pour les URL, les noms de fichiers, de programmes et de répertoires cités dans le texte (autrement dit, quand ils ne sont pas inclus dans du code). L’italique est également utilisé pour les termes étrangers et pour la mise en valeur de mots ou d’expressions importants.

12

CONTENTS

Chapter 1

Introduction à XML et XSLT
Sommaire
1.1 1.2 L’application et ses besoins . XML, format universel . . . . 1.2.1 Qu’est-ce que XML ? . 1.2.2 Structuration avec XML 1.2.3 Documents XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 14 15 15 18 21 21 31 38 42 42 43 44 45 46 51

1.3

1.4

1.5

Publication de données avec XSLT . . . . . . . . . . 1.3.1 Site web (HTML) . . . . . . . . . . . . . . . . 1.3.2 Site WAP (WML) . . . . . . . . . . . . . . . . 1.3.3 Document papier (PDF) . . . . . . . . . . . . . Échange et intégration de données en XML . . . . . 1.4.1 Exemple : Le Site www.sallesenligne.com . . . 1.4.2 Description de la structure d’un document XML 1.4.3 Transformation et échange de données . . . . . 1.4.4 Un moteur de recherche XML/XSLT . . . . . . 1.4.5 Intégration dynamique de fragments XML . . . Comment lire la suite de ce livre ? . . . . . . . . . . .

Ce chapitre a pour ambition de proposer au lecteur une présentation intuitive des principales caractéristiques de XML, ainsi qu’un tour d’horizon de ses apports en terme d’intégration, d’échange et de publication 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 investir 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

1.1

L’application et ses besoins

Décrivons tout d’abord l’application (simplifiée). L’objectif général consiste, pour un cinéma, à diffuser le plus largement possible l’information relative à ses salles, avec les films qui y sont diffusés et les horaires des séances. Nous prendrons comme exemple principal le cas du cinéma L’Épée de bois qui propose deux films :

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 information – 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.

1.2

XML constitue un moyen de rendre un même contenu accessible à plusieurs applications. Considérons le cas des informations propres au cinéma, à savoir son nom, son adresse et la station de métro la plus proche : L’Epée de bois, 100 rue Mouffetard, métro Censier-Daubenton 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.

   

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.

XML, format universel

1.2. XML, FORMAT UNIVERSEL

15

1.2.1

Qu’est-ce que XML ?

XML est donc d’abord destiné à représenter des contenus indépendamment de toute application. Il s’appuie pour cela sur la combinaison de plusieurs principes simples et généraux pour la représentation et l’échange d’information. Voici une représentation XML de notre cinéma : Exemple 1 Représentation XML d’un cinéma <?xml version="1.0" encoding="ISO-8859-1"?><CINEMA><NOM>Epée de Bois </NOM><ADRESSE>100, rue Mouffetard</ADRESSE><METRO>Censier-Daubenton </METRO></CINEMA> Une information codée en XML est donc simplement représentée sous forme d’une chaîne de caractères. Cette chaîne débute obligatoirement par une déclaration XML : <?xml version="1.0" encoding="ISO-8859-1"?> 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 Standardisation (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.

1.2.2

Structuration avec XML

La représentation sous forme de chaîne de caractères n’exclut pas la structuration de l’information. Par exemple, la chaîne « L’adresse du cinéma Épée de Bois est 100, rue Mouffetard et se trouve près de la station de métro Censier-Daubenton » 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 ;

100, rue Mouffetard ;

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 nom de la balise est le type de l’élément ;

le contenu d’un élément XML est obtenu en enlevant les balises qui l’entourent.
est un langage de balisage structurel.

1 XML

¤

¤

£

£ ¤ ¤

£ ¤

£ ¤

£ ¤ £

¤

¤

¤

£

£

£

£

¤

¤

¤

£

£ ¤

£ ¤

£

£ ¤

¤

            £

£

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 : Exemple 2 Représentation XML avec indentation <?xml version="1.0" encoding="ISO-8859-1"?> <CINEMA> <NOM>Epée de Bois</NOM> <ADRESSE>100, rue Mouffetard</ADRESSE> <METRO>Censier-Daubenton</METRO> </CINEMA> 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 clarifier 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

NOM

ADRESSE

METRO

Épée de bois

100, rue Mouffetard

Censier-Daubenton

Figure 1.1: Représentation arborescente d’un contenu XML

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> <SALLE NO=’1’ PLACES=’320’> <TITRE>Alien</TITRE> <REMARQUE>Reservation conseillée</REMARQUE> <SEANCES> <SEANCE>15:00</SEANCE> <SEANCE>18:00</SEANCE> <SEANCE>21:00</SEANCE> </SEANCES> </SALLE> <SALLE NO=’2’ PLACES=’120’> <TITRE>Vertigo</TITRE> <SEANCES> <SEANCE>22:00</SEANCE> </SEANCES> </SALLE> </CINEMA>

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

NOM

ADRESSE

METRO

SALLE NO=1, PLACES=320

SALLE NO=2, PLACES=120

100, rue Mouffetard Épée de bois Censier Daubenton TITRE SEANCES

TITRE

REMARQUE

SEANCES

Vertigo

SEANCE

Réservation conseillée Alien SEANCE SEANCE SEANCE 22:00

15:00

18:00

21:00

Figure 1.2: Arbre d’un document XML complété

1.2.3

Documents XML

Jusqu’à présent nous avons parlé de « chaîne de caractères » sans faire d’hypothèses sur l’origine ou le stockage de cette chaîne : cette dernière peut être placée dans un fichier, mais il peut également s’agir d’un flux (ou message) échangé entre deux applications, ou d’une information engendrée à partir d’une application. Par exemple, la plupart des systèmes de bases de données actuels permettent de générer des documents XML à partir des données stockées. Dans le cas d’un stockage fichier, l’information codée en XML peut être affichée et modifiée par un outil de traitement de texte standard. Il est ainsi possible de visualiser et modifier un document XML très facilement et sans outil sophistiqué. Pour des raisons de simplicité, c’est la situation que nous envisagerons prioritairement dans ce qui suit. Le caractère persistant (cas d’un fichier) ou transitoire (cas d’un message) d’une information codée avec XML sont cependant des aspects secondaires tant qu’on ne s’intéresse pas à des problèmes d’architecture sur lesquels nous reviendrons plus tard. Nous utiliserons uniformément dans ce qui suit le terme de document XML pour désigner un contenu structuré avec des balises XML, et ce quelle que soit la nature physique et la durée d’existence de ce contenu. La notion de document en XML est un peu plus complète que celle d’un arbre d’éléments. La déclaration XML, ainsi que certaines autres informations qui apparaissent avant l’élément racine sont considérées comme parties intégrantes du document. Nous distinguerons donc soigneusement, à partir de maintenant :

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 à

   

l’élément racine, défini par la première balise rencontrée ; la racine du document qui comprend, outre l’élément racine, un ensemble de déclarations et d’instructions utiles pour l’interprétation du contenu.

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"?> <SALLE NO=’2’ PLACES=’120’> <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> <SEANCES> <SEANCE>22:00</SEANCE> </SEANCES> </SALLE>

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 2 du cinéma Epée de Bois - ->

SALLE NO=’2’ PLACES=’120’

FILM

TITRE

AUTEUR

ANNEE

GENRE

PAYS

RESUME

Vertigo

Hitchcock

1958

Drame

USA

Scotty...

Figure 1.3: Arbre d’un document XML complété

¤

£

¤

£

SEANCES

SEANCE

22:00

20

CHAPTER 1. INTRODUCTION À XML ET XSLT

À 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 : Exemple 5 Epee.xml : Fichier constituant par assemblage un document sur le cinéma
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml-stylesheet href="Cinema.xsl" type="text/xsl"?> <?xml-stylesheet href="CinemaWML.xsl" type="text/xsl" media="wap"?> <?cocoon-process type="xslt"?> <!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> <!-- Inclusion du fragment XML décrivant la salle 1 --> &salle1; <!-- Inclusion du fragment XML décrivant la salle 2 --> &salle2; </CINEMA>

L’entité document Epee.xml contient dans son entête deux définitions d’entité externes vers les fichiers Salle1.xml et Salle2.xml : <!ENTITY salle1 SYSTEM "Salle1.xml">\\ <!ENTITY salle2 SYSTEM "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 ; : <!-- Inclusion du fragment XML décrivant la salle 1 --> &salle1; <!-- Inclusion du fragment XML décrivant la salle 2 --> &salle2; Dans la représentation arborescente du document XML (figure 1.2), toutes les références vers ces entité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

1.3

Publication de données avec XSLT

Nous allons maintenant montrer comment, à partir d’un document XML proposant un contenu structuré, on obtient avec des transformations XSLT des publications de ce contenu destinées aux supports les plus variés. L’idée de base d’un environnement XML/XSLT est de séparer le traitement des données du processus de publication.

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 :

1.3.1

HTML est un langage de balisage dédié à l’échange de documents sur le Web sous forme de document hypertextes2 dans lesquels certains mots ou groupes mots (les ancres) servent d’accès à d’autres documents HTML, parfois situés sur d’autres sites web. L’utilisation des ancres permet de « naviguer » sur le Web sans même connaître ses notions de base (le protocole HTTP, le système d’adressage par des URL, etc). Contrairement à XML, HTML fournit un ensemble fixe de balises auxquelles sont attachées des fonctionnalités précises de présentation. L’utilisation des balises HTML est donc avant tout destinée à donner des directives de mise en forme au navigateur qui se charge d’afficher le document. À l’opposé, l’auteur d’un document XML définit son propre vocabulaire de balises, le choix étant guidé par le besoin de décrire au mieux possible la structure et la signification des données. Nous reviendrons en détail sur le langage HTML, ses différentes versions, et ses rapports avec XML. Ici nous nous contenterons de montrer comment nous pouvons obtenir une version HTML de nos documents XML par transformation XSLT, en commençant par prendre l’exemple très simple de la description d’un film. Voici le document HTML que nous désirons obtenir. Exemple 6 Vertigo.html : Document HTML pour le film Vertigo :
<html> <head> <title>Film: Vertigo</title> </head> <body bgcolor="white"> <img src="Vertigo.gif" height="220" align="left"></img> <h1><i>Vertigo</i></h1> Drame, <i>Etats Unis</i>, 1958 <p> Mis en scène par <b>Alfred Hitchcock</b></p> <h3>Résumé</h3> Scottie Ferguson, ancien inspecteur de
2 « HTML »

         

le traitement des données consiste à les mettre sous forme de document XML obéissant à une structure 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 format reconnu par une application de publication particulière (par exemple au format HTML reconnu par les navigateurs web).

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é.

Site web (HTML)

est l’abréviation de « HyperText Markup Language ».

22

CHAPTER 1. INTRODUCTION À XML ET XSLT

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... </body> </html>

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 :

inclure des images (balise

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 navigateurs 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.

Figure 1.4: Page HTML affiché avec Netscape 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

¤

£

¤ £ ¤ £

créer des paragraphes (balise

p );

img ) qui sont stockées dans des fichiers externes.

¤ £

¤ £

mettre du texte en italique (balise

i ) ou en gras (balise

b );

¤

£

¤

£ ¤

£

différencier différents niveaux de titre de sections (balises

h1 ,

¤

£

centrer une partie d’un document en utilisant la balise

center ; h2 , . . . , h5 ) ;

¤

£

¤

£

¤

         

£

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.

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 distinguer 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.

¤

£ ¤

¤

¤

¤

£

£ ¤

£

£

¤

£

£

¤

¤

    £ £

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.

24 select="TITRE"

CHAPTER 1. INTRODUCTION À XML ET XSLT
Contexte d’application de la règle XSLT
FILM

TITRE

AUTEUR

ANNEE

GENRE

PAYS

RESUME

Vertigo

Hitchcock

1958

Drame

USA

Scotty...

Figure 1.5: Extraction des informations dans une règle XSLT

Nous sommes maintenant en mesure d’enrichir notre première règle pour produire un premier document 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> L’élément xsl:value-of select="TITRE"/ est remplacé à l’exécution par la valeur de l’élément TITRE , soit Vertigo. Appliquée à Vertigo.xml, cette règle produira donc le résultat suivant : <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.

Notre objectif est donc d’obtenir le document HTML de l’exemple 6 par transformation XSLT du document Vertigo.xml ci-dessous. Exemple 7 Vertigo.xml : Le document XML pour le film Vertigo

¤

Transformation XML -

HTML

¤

¤

£

£

1.3. PUBLICATION DE DONNÉES AVEC XSLT
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml-stylesheet href="Film.xsl" type="text/xsl"?> <?xml-stylesheet href="FilmWML.xsl" type="text/xsl" media="wap"?> <?cocoon-process type="xslt"?> <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>

25

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. Exemple 8 Film.xsl : Programme de transformation XSLT correpondant à la Figure 1.6.
<?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="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 TITRE AUTEUR ANNEE GENRE PAYS RESUME

sele
Vertigo Hitchcock 1958 Drame

ct="

AUT E

UR"

USA

Scotty...

se

lec

t=

"R

ES

UM

E"

selec t="T ITR E"

" EE NN ="A ect sel select="PA YS"

select="

select="TITRE

xsl:value-of

GENRE

select="TIT RE"

Résumé:

xsl:value-of

"

Film:

title

"

xsl:value-of i img h1

xsl:value-of xsl:value-of i

Mise . . .

b

h3

xsl:value-of

xsl:value-of

p

p

head html xsl:template match="FILM"

body

Figure 1.6: Transformation XSLT

1.3. PUBLICATION DE DONNÉES AVEC XSLT
<i><xsl:value-of select="TITRE"/></i> </h1> <!-- Genre, pays, et année du film --> <xsl:value-of select="GENRE"/>, <i> <xsl:value-of select="PAYS"/></i>, <xsl:value-of select="ANNEE"/> </p> <p> <!-- Auteur du film --> Mis en scène par <b><xsl:value-of select="AUTEUR"/></b> <!-- Résumé du film --> <h3>Résumé</h3> <xsl:value-of select="RESUME"/> </p> </body> </html> </xsl:template> </xsl:stylesheet>

27

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 ;

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

¤

enfin l’élément REMARQUE (voir figure 1.3, page 19).

est optionnel : il apparaît pour la salle 1, mais pas pour la salle 2

¤

£

¤

£

£

l’élément de fois ;

SEANCE

est petit-fils de

SALLE , et peut de plus être répété un nombre quelconque

¤

£

¤

£

¤

£

les informations décrivant le film sont des petits-fils de sont séparés par FILM ;

SALLE :

¤

£

¤

£ ¤

¤

£

le nœud a trois fils :

FILM ,

REMARQUE

et

SEANCES ; TITRE et AUTEUR en

¤

¤

£ £ ¤

£

£

¤

£

         

28

CHAPTER 1. INTRODUCTION À XML ET XSLT
Contexte d’application de la règle SALLE chemin "FILM/TITRE" NO=2 PLACES=320 chemin SEANCES/SEANCE FILM chemin "REMARQUE" TITRE AUTEUR ANNEE GENRE PAYS RESUME REMARQUE SEANCES chemin "@NO"

Alien

Scott Science-Fiction

1979

USA

Près d’un Réservation vaisseau spatial... conseillée

boucle for-each

SEANCE

SEANCE

SEANCE

15:00

18:00

21:00

Figure 1.7: Le document XML pour la première salle 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"/> <!-- Boucles sur toutes les séances <h3>Séances</h3> <ol> <xsl:for-each select="SEANCES/SEANCE"> -->

¤

£

       

1.3. PUBLICATION DE DONNÉES AVEC XSLT
<li> <xsl:value-of select="."/></li> </xsl:for-each> </ol> <xsl:for-each select="REMARQUE"> <font color="red"><xsl:value-of select="."/></font> </xsl:for-each> </xsl:template>

29

L’instruction xsl:for-each permet de créer une boucle pour ajouter toutes les séances et remarques dans le résultat. La transformation définie par le contenu d’un élément xsl:for-each ne s’applique pas à l’élément qui est en cours de transformation (la salle), mais aux éléments sélectionnés par l’attribut select (les séances). Ainsi, l’instruction xsl:value-of select="."/ dans la première boucle sera remplacé par le contenu de chaque séance retrouvée. 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 document 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:applytemplates. 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 select="SALLE"

AUTEUR

ANNEE

GENRE

PAYS

RESUME

SEANCE

Vertigo

Hitchcock

1958

1958

USA

Scotty...

22:00

...
head

xsl:apply-templates "Salle No" body h2 h3 xsl:value-of "Capacité:" xsl:value-of

"places"

html

...
<xsl:template match="/"> <xsl:template match="SALLE">

Figure 1.8: Appel de règles à partir d’autres règles par application de la règle destinée à présenter une salle individuelle. Pour créer la présentation d’un cinéma, nous allons donc reprendre la règle existante de traitement des éléments SALLE, et déclencher son application. On utilise pour cela un élément xsl:applytemplate. Voici tout d’abord la règle s’appliquant à l’élément racine du document XML.
<xsl:template match="/"> <html> <head><title>Programme de <xsl:value-of select="CINEMA/NOM"/> </title> </head> <body bgcolor="white"> <xsl:apply-templates select="CINEMA"/> </body> </html> </xsl:template>

La seconde règle d’applique dans le contexte d’un élément CINEMA . Elle extrait des informations relatives au cinéma, et propage la production de code HTML par un nouvel appel à xsl:applytemplates. <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

Figure 1.9: Copie écran du résultat de la transformation de Salle1.xml

<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:applytemplates 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. 1. la première règle, appliquée à la racine du document XML, produit un « squelette » de document HTML, avec les trois balises html , head et body indispensables, puis elle passe la main à la règle s’appliquant aux éléments de type CINEMA ; 2. la seconde règle produit les informations globales sur un cinéma, puis déclenche l’application de la règle aux éléments SALLE ; 3. enfin la troisième règle s’applique aux deux éléments SALLE présents dans la description du cinéma L’Épée de bois, et produit les deux sous-arbres HTML décrivant ces deux salles. 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é.

1.3.2

Site WAP (WML)

Passons maintenant à la création d’une représentation permettant de consulter les séances à partir d’un téléphone mobile, en partant du même document XML décrivant les séances à « L’Epée de bois ». Publication WAP (WML) Le langage utilisé pour spécifier l’affichage sur un mobile est WML. Il s’agit d’un « dialecte », ou spécialisation de XML, qui se base sur un ensemble de noms d’éléments et définit leur signification.

¤

£

¤

¤

£

£

¤

¤

£ ¤

£

£

¤

£

¤

£

32

CHAPTER 1. INTRODUCTION À XML ET XSLT

Figure 1.10: La page d’accueil de L’Épée de bois 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. Exemple 9 Alien.wml : Un document WML
<?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"> <wml> <card> <p><b>Alien</b>, 1979, Ridley Scott<br/> Près d&apos;un vaisseau spatial échoué sur une lointaine planète, des Terriens en mission découvrent de bien étranges &quot;oeufs&quot;. Ils en ramènent un à bord, ignorant qu&apos;ils viennent d&apos;introduire parmi eux un huitième passager particulièrement féroce et meurtrier. </p> </card> </wml>

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 Règle "/"

h1

hr

adresse

p

p

i h2 Epée de bois Salle No 1 Règle "CINEMA" Règle "SALLE" Film: Alien Salle No 2 Film : Vertigo h3 h2 h3

Figure 1.11: L’arbre HTML, résultat de la transformation XSLT 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. Exemple 10 Alien.xml : Document XML pour le film Alien
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml-stylesheet href="FilmWML.xsl" type="text/xsl" media="wap"?> <?cocoon-process type="xslt"?> <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 :

à déclencher, dans la balise principale, un appel aux autres règles.

Voici cette première règle.

¤

£

à ouvrir, puis refermer la balise principale

wml ;

¤

£ ¤

¤ £

£ ¤

£

¤

£

   

34

CHAPTER 1. INTRODUCTION À XML ET XSLT

Figure 1.12: L’affichage de la carte de l’exemple 9 <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). Exemple 11 FilmWML.xsl : La feuille XSLT créant une version WML

¤

£

1.3. PUBLICATION DE DONNÉES AVEC XSLT
<?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <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>

35

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. Création d’un site WAP Nous pouvons maintenant, à partir de notre document XML décrivant L’Epée de bois, créer un ensemble de cartes permettant de choisir la salle et le film, de consulter les séances pour cette salle, et enfin d’afficher les informations détaillées sur le film. La structure du document WML résultat est donnée dans la figure 1.13. Il contient cinq éléments card : un pour le menu d’accueil, montrant les salles de L’Epée de bois et le film qui y est projeté, deux cartes pour les deux salles, enfin deux cartes donnant des détails sur les films. Pour permettre la navigation dans ce document, chaque carte doit être identifiée par la valeur – unique au sein du document WML – de l’attribut id dans la balise card . La figure 1.13 montre les id de chaque carte : index, S1, S2, Alien et Vertigo. Pour créer un lien vers une carte, on utilise en WML, comme en HTML, la balise a . Voici par exemple l’élément WML permettant de référencer la carte décrivant Alien : <a href="#Alien">lien vers le film Alien</a> 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

card

id=index

card

id=S1

card

id=S2

card

id=Alien

card

id=Vertigo

p href=S1

p href=Alien a href=S2

p href=Vertigo a

p

p

a

a

Figure 1.13: L’arbre XML du site WAP <wml> <!-- création de la carte d’accueil <xsl:apply-templates select="CINEMA"/>

-->

<!-- création des cartes pour les salles et séances --> <xsl:apply-templates select="CINEMA/SALLE"/> <!-- création des cartes pour les films <xsl:apply-templates select=".//FILM"/> </wml> -->

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 : 1. prendre en compte la position courante dans l’arbre ; 2. indiquer le chemin à suivre dans l’arbre, à partir de la position courante, pour atteindre l’élément à traiter. Ici la position courante (autrement dit à laquelle la règle courante s’applique) est la racine du document, 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 :

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. l’élément

CINEMA

est un fils de la position courante : on le désigne simplement par CINEMA ;

¤

¤

£

£

¤

£

£

1.3. PUBLICATION DE DONNÉES AVEC XSLT

37

Figure 1.14: Un téléphone mobile avec carte WML

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 (exemple 11).

1.3.3

Document papier (PDF)

Chaque type de document a des contraintes spécifiques – affichage, mode d’utilisation – définies par son interface utilisateur. L’auteur doit prendre en compte la taille de la fenêtre d’affichage, la couleur des pages, la couleur et la taille des caractères, la possibilité – ou non – d’effectuer un défilement, etc. Ces critères sont très stricts par exemple pour un téléphone portable dont l’écran est très petit et les manipulations limitées par un clavier réduit à une quinzaine de touches. La création d’un document destiné à l’impression est essentiellement différente de ceux destinés au Web et constitue un changement radical de support. Une page HTML est affichée sur un écran informatique et doit avoir une taille limitée, approximativement égale à la taille de l’écran. De plus la lecture de documents HTML n’est pas fondée sur un parcours linéaire, de bas en haut et de gauche à droite, mais sur une « navigation » définie par le hyper-liens. Par contraste, un document papier est organisé en une séquence de pages, avec un ordre de lecture séquentielle. En conséquence, quand on imprime un document HTML, le résultat est souvent insatisfaisant. Nous complétons cette présentation de l’utilisation de XSLT pour la transformation de documents XML avec la production de documents papier grâce aux formatting objects. Les formatting objects Le processus de publication permettant de contrôler le résultat et la qualité de l’impression d’un document XML s’effectue en deux étapes (figure 1.15) : 1. L’étape de transformation permet de revoir complétement la structure du document XML en appliquant des règles de transformation. Les éléments d’origine peuvent être remplacés par d’autres éléments, déplacés, effacés, et de nouveaux éléments peuvent être insérés. On obtient un nouveau document. 2. L’étape de mise en forme (formatting en anglais) est appliquée après la transformation pour obtenir un document final adapté au support de publication. Dans le cas d’un document papier, la mise en forme prend en compte la taille des pages, les marges à respecter, la taille et le poids des caractères, l’alignement du texte, l’espace entre les paragraphes, l’en-tête et le pied de page, etc. La recommendation XSL propose un vocabulaire de balises XML, connu sous le nom XSL-FO (FO est un acronyme pour formatting objects), qui permet la spécification précise des caractéristiques typographiques d’un document. Un document XSL-FO est un document XML qui peut être utilisé par différentes applications pour générer un document dans un format d’impression comme Postscript ou PDF. Voici un premier document XSL-FO :

¥

¥

1.3. PUBLICATION DE DONNÉES AVEC XSLT
Document XML Document XSL-FO Document PDF

39

Transformation

Figure 1.15: Transformation et Mise en Forme Exemple 12 SimpleFO.xml : Un document XSL-FO simple
<?xml version="1.0" encoding="iso-8859-1"?> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="29.7cm" page-width="21cm"> <fo:region-body margin-top="2cm" margin-bottom="2.5cm" margin-left="2.5cm" margin-right="2.5cm"/> </fo:simple-page-master> <fo:page-sequence-master master-name="simple" > <fo:single-page-master-reference master-name="page" /> </fo:page-sequence-master> </fo:layout-master-set> <fo:page-sequence master-name=’simple’> <fo:flow flow-name=’xsl-region-body’ font-size="20pt"> <fo:block> Ceci est le premier paragraphe, </fo:block> <fo:block space-before="20pt" font-size="10pt"> et ceci est le deuxième. </fo:block> </fo:flow> </fo:page-sequence> </fo:root>

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 (spacebefore=”20pt” et la taille des caractères (font-size=”10pt”).

12 21 12 1 12 21 12 1

0 0 "! "! "! 0) )0 !"! ) !"! #$  !"!  ) ) ) #$#$   %%%%%&  & & & & #$  %&% & & & & & %%%%%%& %
Mise en Forme

5

5

© © ¨© ¨© ¨ ¨ ¨ ¨ '( '( '            '(  (   ' '               5 5 4 

               ¦§ ¦§ ¦§ ¦§ ¦§ ¦§ ¦§ ¦§ 4 4 4 3 3 3 3 

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 (spacebefore="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. Le programme de L’Épée de bois On peut maintenant insérer les informations concernant notre cinéma Epée de bois. Pour cela on va définir deux règles de transformation. La première met en forme le nom et l’adresse du cinéma.
<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 -->

5

5

4 4

1.3. PUBLICATION DE DONNÉES AVEC XSLT

41

40pt, gras

2cm

26pt, gras 20pt

20pt, italique

2.5cm

2.5cm

21cm

Figure 1.16: Mise en Forme avec XSL-FO

Salle <xsl:value-of select="@NO"/> </fo:inline> (<xsl:value-of select="@PLACES"/> places) : <!-- Sélection des séances --> <xsl:for-each select="SEANCES/SEANCE"> <xsl:value-of select="."/> </xsl:for-each> <!-- Sélection de la remarque --> <xsl:value-of select="REMARQUE"/> </fo:block> </xsl:template>

Ces deux règles effectuent simultanément la transformation de la structure du document XML et la mise en forme du résultat sous forme d’un document XSL-FO. Le document XSL-FO peut ensuite être transformé par un transformateur adapté XSL-FO PDF, avec le résultat de la figure 1.16. En résumé, à partir d’un même document XML, nous sommes en mesure de produire avec un seul langage des représentations très diverses.

2.5cm

29.7cm

6

42

CHAPTER 1. INTRODUCTION À XML ET XSLT

1.4

Échange et intégration de données en XML

La technologie XML n’est pas limitée à la génération de pages web (HTML, WAP) ou d’autres documents plus traditionels. Par sa capacité de représenter des données très diverses, XML a rapidement été promu comme format universel pour l’échange de données entre différéntes applications informatiques. Dans cette section nous décrivons l’utilisation de XML comme interface entre plusieurs applications souhaitant échanger des données. Notre exemple va montrer l’intérêt de XML comme format d’échange entre serveurs web, mais il s’extrapole facilement à l’utilisation de XML pour échanger des informations sous forme de messages entre des applications réparties. Nous mettons plus l’accent sur le processus de transformation impliqué dans ces échanges que sur la transmission des données qui est liée à l’infrastructure client-serveur et aux protocoles de communication 4.

1.4.1

Exemple : Le Site www.sallesenligne.com

L’utilisation de XML comme format d’échange est illustrée par un site web qui récupère des informations sur les programmes de cinéma disponibles sur différents sites locaux, les intègre et fournit un moteur de recherche. La Figure 1.17 montre le site www.sallesenligne.com, relié à de nombreux sites locaux dont deux, www.cine-marseille et www.epee-de-bois.fr, sont représentés.

réponse www.cine-marseille.fr

requête www.sallesenligne.fr

www.epée-de-bois.fr

Figure 1.17: Échanges de données du site www.sallesenligne.com 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.

5

4

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.

1.4.2

Description de la structure d’un document XML

Pour écrire un programme de transformation, il faut connaître la structure du document à transformer, mais également la structure du document à engendrer. Comme dans les exemples sur la publication, où la structure du résultat d’une transformation devait être conforme aux « formats » HTML, WML ou XSL-FO, l’information exploitée par le moteur de recherche doit « ressembler » à la structure de l’élément FILM précédent. En l’occurrence, le serveur central veut acquérir des éléments de type FILM, avec des sous-éléments de type TITRE, CINEMA, VILLE, URL et HEURE. Chaque sous-élément n’apparaît qu’une seule fois sauf les sous-éléments de type HEURE. Cette description informelle de la structure des documents XML échangés peut être utilisé pour écrire un programme XSLT qui génère des « résumés » à partir des documents stockées sur les sites locaux. Pour éviter les malentendus et ambiguités qui sont possibles dans un langage naturel, il existe des langages plus formels pour décrire la structure d’un document XML. Nous introduisons ci-dessous la méthode la plus répandue à l’heure actuelle pour définir des types de documents : les document type definition ou DTD. Dans une DTD, la structure d’un document XML est spécifiée par la description structurelle des types d’éléments. Ainsi, à travers une DTD, un élément n’est plus seulement caractérisé par le nom de la balise, mais également par la structure de son contenu. Par exemple, l’expression <!ELEMENT TITRE ( #PCDATA ) > indique que le contenu (aussi appelé modèle de contenu) d’un élément de type TITRE est une chaîne de caractères (#PCDATA), comme les éléments de type TITRE, CINEMA, VILLE, URL et HEURE. Le contenu des éléments de type FILM est défini par une expression plus complexe : <!ELEMENT FILM (TITRE, CINEMA, VILLE, URL?, HEURE+) 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 :

5

5

4

4

5

4

44

CHAPTER 1. INTRODUCTION À XML ET XSLT

Exemple 13 Echange.dtd : DTD des documents acceptés par le moteur de recherche.
<!ELEMENT FILM <!ELEMENT <!ELEMENT <!ELEMENT <!ELEMENT <!ELEMENT TITRE CINEMA VILLE URL HEURE ( TITRE, CINEMA, VILLE, URL?, HEURE+ ) > ( #PCDATA ) > ( #PCDATA ) > ( #PCDATA ) > ( #PCDATA ) > ( #PCDATA ) >

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.

1.4.3

Transformation et échange de données

L’application – le site central – traite des documents conformes à la DTD Echange.dtd, et les intègre dans une liste formant un index de toutes les séances de cinémas. La structure de ce document est très simple : l’élément racine de type MOTEUR contient un sous-élément FILM pour chaque film projeté dans un cinéma. Notez dans l’en-tête la référence à la DTD Moteur.dtd qui décrit la structure du document. Exemple 14 Moteur.xml : Document XML interrogé par le moteur de recherche
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml-stylesheet href="MoteurCherche.xsl" type="text/xsl"?> <?cocoon-process type="xslt"?> <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>

5

4

1.4. ÉCHANGE ET INTÉGRATION DE DONNÉES EN XML
</FILM> </MOTEUR>

45

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. Exemple 15 Echange.xsl : Transformation XSLT pour échange de données.
<?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"/> <xsl:output encoding="iso-8859-1"/> <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 transformation 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.

1.4.4

Un moteur de recherche XML/XSLT

Pour conclure cet exemple, nous allons décrire l’implantation d’un petit moteur de recherche pour trouver des séances de cinéma dans un document XML. Nous supposons que toutes les informations sont stockées dans Moteur.xml. 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
</xsl:for-each> </xsl:template>

CHAPTER 1. INTRODUCTION À XML ET XSLT

<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 &gt;= $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 différentes manières. Dans le cas d’une application web, on utilise naturellement un formulaire HTML (figure 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 documents 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.

1.4.5

Intégration dynamique de fragments XML

Dans tous les exemples que nous avons étudiés jusqu’à présent les informations étaient issues de sources XML, éventuellement transformées au passage par XSLT. Nous allons conclure ce tour d’horizon – partiel

5

4

1.4. ÉCHANGE ET INTÉGRATION DE DONNÉES EN XML

47

– des possibilités de publication à partir de XML/XSLT en étudiant la situation où la représentation XML est produite dynamiquement à partir d’une autre source de données. Cette notion de dynamicité est maintenant bien établie dans le domaine des sites web. Les sites constitués de pages HTML au contenu figés sont en effet extrêmement difficiles à maintenir, et tout à fait inappropriés pour interagir avec un internaute qui vient visiter le site, y rechercher des informations, voire déposer ses propres données. La plupart des sites professionnels sont donc associés à une base de données qui gère l’information indépendamment de toute représentation, les données étant extraites et mises en forme « à la volée » par des techniques de programmation spécialisées basées sur l’interface CGI (Common Gateway Interface). Les principaux langages dédiés à ce type d’application sont Perl, PHP et Java, ce dernier sous la forme de servlets ou de leur dérivé, les Java Server Pages (JSP). Dans le cas de PHP ou des JSP, le principe consiste à intégrer dans un document HTML des parties programmées qui vont se connecter à la base de données (ou à toute autre source d’information), effectuer des requêtes SQL et formater le résultat de ces requêtes avec des balises HTML. Il est tout à fait souhaitable de pouvoir faire de même avec XML, dans la mesure où on admet qu’un système d’information ne gére pas toutes ses données sous forme XML, mais fait appel à des applications spécialisées : traitement de texte, tableur, annuaire, et bien sûr SGBD. Notons que PHP ou les JSP sont – du moins à l’heure actuelle – conçus pour être utilisés uniquement avec HTML. Le scénario que nous considérons ici est plus général, et se déroule en deux étapes. Dans un premier temps l’organisme ou la société qui souhaite publier une partie du contenu de son système d’information intégre ce contenu sous une forme homogène XML. Puis, à partir de cette intégration les différents types de présentation étudiés précédemment (HTML, WML, PDF, etc) sont générés par des feuilles de styles XSLT. Nous prenons dans ce qui suit l’exemple d’une intégration dynamique (autrement dit au moment où le document est utilisé) à partir d’une base de données relationnelle. Cette base contient une table Film dont nous allons extraire une ou plusieurs lignes. titre Alien auteur Ridley Scott année 1979 genre USA pays Science-Fiction résumé Près d’un vaisseau spatial échoué sur une lointaine planète, des Terriens en mission découvrent de bien étranges "oeufs"... Scottie Ferguson, ancien inspecteur de police, est sujet au vertige depuis qu’il a vu mourir son collègue... ...

Vertigo

Alfred Hitchcock

1958

Drame

USA

...

...

...

...

...

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 connexion 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 statique Balises "dynamiques" Partie statique Document initial Code SQL Base de données

Partie statique Publication <fragment XML> Partie statique Document intermédiaire XSLT

Figure 1.19: Architecture d’une intégration XML/Base de données d’exécuter du code Java inclus dans un document XML. Le mécanisme peut être transposé, mutatis mutandis, à d’autres environnement, par exemple l’environnement XML proposé par Oracle qui propose sensiblement 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. D’une base relationnelle à XML Voici pour commencer le code Java qui permet d’accéder à la base MySQL, d’effectuer la requête SQL recherchant un film (ici c’est Alien) et de le mettre au format XML. Le petit programme ci-dessous utilise l’interface JDBC (Java Database Connectivity) pour se connecter à MySQL. Exemple 17 ExJDBC.java : Code Java pour extraire un film au format XML
// D’abord on importe le package JDBC import java.sql.*; 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"); // Exécution de la requête qui ramène le film Alien Statement stmt = conn.createStatement (); ResultSet rset = stmt.executeQuery ("select * from Film where titre=’Alien’"); // Sortie du résultat avec balises XML while (rset.next ()) { System.out.println ("<FILM>"); System.out.println ("<TITRE>" + rset.getString (1) + "</TITRE>");

1.4. ÉCHANGE ET INTÉGRATION DE DONNÉES EN XML
System.out.println System.out.println System.out.println System.out.println System.out.println System.out.println ("<ANNEE>" + rset.getString (2) + "</ANNEE>"); ("<AUTEUR>" + rset.getString (3) + "</AUTEUR>"); ("<GENRE>" + rset.getString (4) + "</GENRE"); ("<RESUME>" + rset.getString (5) + "</RESUME>"); ("<PAYS>" + rset.getString (6) + "</PAYS>"); ("</FILM>");

49

} } catch (SQLException e) { System.out.println ("Problème quelque part !!!"); System.out.println (e.getMessage()); } } }

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 interfaces entre un langage de programmation et SQL. La classe ResultSet propose un ensemble de méthodes 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 : Exemple 18 ExJDBC.xml : Résultat de l’exécution du programme java
<FILM> <TITRE>Alien</TITRE> <ANNEE>1979</ANNEE> <AUTEUR>Ridley Scott</AUTEUR> <GENRE>Science-fiction</GENRE <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> <PAYS>USA</PAYS> </FILM>

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. Exemple 19 AlienXSP.xml : La version XML/XSP incluant du code java
<?xml version="1.0" encoding="ISO-8859-1"?>

50

CHAPTER 1. INTRODUCTION À XML ET XSLT

<?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 : 1. l’appel aux packages nécessaires, obtenu avec le type d’élément xsp:include ; 2. l’inclusion directe de code Java dans les éléments de type xsp:logic ; 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 ?
Séparation des points de vue

51

Il reste cependant un aspect insatisfaisant dans cette séparation des rôles : en incluant directement du code Java dans du XML, on mélange toujours la gestion du contenu et la « logique » de l’application. Autrement dit la personne qui est en charge de définir les informations à publier et leur structure, est également confrontée au code qui permet de produire ces informations. Idéalement ces deux points de vues devraient également être séparés. C’est ce que permet l’utilisation de balises « dynamiques ». En voici un exemple : Exemple 20 AlienXSPLib.xml : Le document pour Alien avec balise dynamique
<?xml version="1.0" encoding="ISO-8859-1"?> <?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 temporaire 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 ; 3. enfin la présentation est obtenue par un ensemble de programme XSLT.

1.5

Comment lire la suite de ce livre ?

Le moment est venu de récapituler ce premier chapitre, et de voir comment il fournit la clé pour appréhender le contenu de ce livre, et donc en guider la lecture. 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. Échange et intégration avec XML Le rôle de XML comme langage d’échange et d’intégration de données découle des caractéristiques qui précèdent. Dans beaucoup de cas, un système d’information confie ses données à des logiciels spécialisés dans une tâche donnée (serveur web, base de données, fichiers de configuration, etc) et les rend du même coup impropres à une utilisation autre que celle à laquelle elles ont été initialement affectées. Transformer ses données au format XML, temporairement ou définitivement, revient à les rendre disponibles pour d’autres applications et d’autres utilisateurs. Intégrer ces informations, issues de sources diverses, dans un format commun, permet à la fois de donner une vision uniforme des informations, et d’éviter de confronter le responsable de la publication à des logiciels et langages divers et complexes. De fait la connaissance de XSLT suffit. 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.

5

5

4

4 5

4 5

4

5

5 4

5

4

4

5 4 5 4 5 4

5

5

4

4

1.5. COMMENT LIRE LA SUITE DE CE LIVRE ?

53

Fichiers

Base de données

Message XML

XSLT

Transformation XSLT

Document XML

XSLT

XSLT

XSLT

XSLT

Page XHTML

Contenu formaté application édition

Page WML

Document XML

application web

application WAP

application XML

Figure 1.20: Intégration XML, et transformations XSLT 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 : 1. une ou plusieurs pages XHTML constituant un site web ; 2. un formatage adapté à un traitement de texte, en vue de faire de l’édition ; 3. des « cartes » WML pour communiquer par le WAP avec des téléphones mobiles ; 4. enfin on peut imaginer toute conversion d’un dialecte XML vers un autre, l’objectif dans ce cas étant de faire communiquer des applications en vue de réutiliser des informations existantes. 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. Organisation des chapitres qui suivent La suite de ce livre reprend de manière systématique tous les thèmes abordés dans ce qui précède. Le chapitre 3 donne une description complète de la syntaxe XML, et introduit les opérations applicables à un document XML. Le chapitre 4 reprend de manière rigoureuse le langage XSLT, tandis que les chapitres 5 et 6 développent l’utilisation de XSLT pour effectuer des transformations vers des DTD courantes : HTML, WML, SMIL, RSS, XSL-FO... Les chapitres suivants continuent l’exploration des transformations XSLT, en y ajoutant des processus d’intégration et d’échanges de données. Nous espérons que la lecture de ce premier chapitre aura permis de clarifier les choix d’organisation qui ont été faits, et donneront l’opportunité au lecteur, en fonction de ses compétences, de choisir éventuellement un ordre de lecture différent.

54

CHAPTER 1. INTRODUCTION À XML ET XSLT

Chapter 2

Documents XML : structure et navigation
Sommaire
2.1 La syntaxe XML . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1 Déclaration XML . . . . . . . . . . . . . . . . . . . . 2.1.2 Déclaration du type de document et des entités . . . . . 2.1.3 Commentaires . . . . . . . . . . . . . . . . . . . . . . 2.1.4 Instructions de traitement . . . . . . . . . . . . . . . . 2.1.5 Éléments . . . . . . . . . . . . . . . . . . . . . . . . 2.1.6 Attributs . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.7 Espaces . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.8 Sections CDATA . . . . . . . . . . . . . . . . . . . . . 2.1.9 Références d’entités . . . . . . . . . . . . . . . . . . . 2.1.10 Balises, données caractères et valeur textuelle . . . . . Le modèle DOM . . . . . . . . . . . . . . . . . . . . . . . . 2.2.1 Types de nœuds DOM . . . . . . . . . . . . . . . . . . 2.2.2 Interface DOMString . . . . . . . . . . . . . . . . . . 2.2.3 Interfaces NodeList et NamedNodeMap . . . . . . . . 2.2.4 Interface Node . . . . . . . . . . . . . . . . . . . . . 2.2.5 Interface Document . . . . . . . . . . . . . . . . . . . 2.2.6 Interfaces Element et Attr . . . . . . . . . . . . . . . 2.2.7 Interfaces DocumentType, Entity et EntityReference Du document sérialisé à l’arbre DOM . . . . . . . . . . . . 2.3.1 Construction d’un arbre DOM . . . . . . . . . . . . . 2.3.2 Traitement des espaces pendant la construction . . . . . 2.3.3 Deux fonctions de navigation . . . . . . . . . . . . . . Le langage XPath . . . . . . . . . . . . . . . . . . . . . . . . 2.4.1 Représentation XPath d’un document XML . . . . . . 2.4.2 Expressions XPath . . . . . . . . . . . . . . . . . . . 2.4.3 Les axes . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.4 Les filtres . . . . . . . . . . . . . . . . . . . . . . . . 2.4.5 Prédicats . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.6 Types et opérations XPath . . . . . . . . . . . . . . . . 2.4.7 Exemples d’expressions XPath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 . 57 . 57 . 58 . 58 . 59 . 59 . 60 . 61 . 61 . 62 . 64 . 64 . 66 . 66 . 66 . 71 . 72 . 74 . 74 . 76 . 78 . 79 . 81 . 82 . 86 . 91 . 98 . 100 . 103 . 107

2.2

2.3

2.4

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 ’&#62;’ et ’&gt;’ dans un document XML. Nous décrirons la structure arborescente d’un document XML en nous appuyant sur un modèle normalisé 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 description 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 pratique 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.

2.1

La syntaxe XML

Les documents XML sont la plupart du temps créés, stockés ou échangés sous une représentation textuelle ou sérialisée qui « marque » la structure par des formes syntaxiques (essentiellement des balises) mêlées au contenu textuel. Nous donnons maintenant les règles syntaxiques de base pour représenter des documents XML sérialisés. Nous verrons dans la section suivante comment on passe de cette représentation à une représentation arborescente sous forme d’un arbre DOM. Commençons par l’exemple d’un document XML dont le contenu – sommaire – résume les principaux aspects syntaxiques du langage. Exemple 21 ExXML4.xml : Illustration de la syntaxe XML
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?> <!DOCTYPE A SYSTEM "minimal.dtd" [ <!ENTITY monTexte "texte simple"> <!ENTITY maSignature SYSTEM "signature.xml"> ]> <!-- Instruction de traitement --> <?xml-stylesheet href="prog.xslt" type="text/xslt"?>

5

4

5

4 75 4

2.1. LA SYNTAXE XML
<A> Du &monTexte;, sans caractères réservés: ni &lt; ni &gt; ni &amp; ni &apos; ni &quot; <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>

57

Cet exemple va nous servir de support à la présentation des différents composants d’un document XML.

2.1.1

Déclaration XML

La déclaration XML est une balise spéciale qui doit apparaître au début du document. Sa forme minimale indique la version de la norme XML à laquelle se conforme le document :
<?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ématiquement 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 standalone=’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.
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>

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.

2.1.2

Déclaration du type de document et des entités

La balise spéciale !DOCTYPE définit la DTD (Définition du Type de Document) qui est une description générale de la structure du document. Cette description peut être entièrement incluse dans la balise sous forme de déclarations locales, mais le plus souvent on fait appel à une source externe (ce peut être un nom de fichier local, ou une URL). La forme générale de la déclaration est alors : !DOCTYPE nomDocument SYSTEM "sourceExt" [decLoc] Cette déclaration indique tout d’abord que le document est de type nomDocument. Après le mot-clé SYSTEM on trouve l’URL sourceExt qui correspond à un fichier avec des déclarations externes sur la structure du document. Les déclarations locales decLoc sont entourées des symboles [ et ]. Un problème bien connu des URL pour identifier des unités d’informations comme une page HTML ou un fichier DTD est l’utilisation d’un mécanisme d’adressage physique. Ce type d’adressage est risqué dans un environnement évolutif où les serveurs Web changent d’adresse et où les fichiers sont déplacés d’un

5

5

4

4

58

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION

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 endroits. 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) :
<!ENTITY <!ENTITY monTexte "texte simple"> maSignature SYSTEM "signature.xml">

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 commentaire suivant est syntaxiquement correct (bien formé) : <!-- Instruction de traitement <?xml-stylesheet ...> --> Les deux commentaires suivants ne sont pas corrects : <!-- erreur: -- n’est pas permis --> <!-- erreur: ---> 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.

2.1.4

Instructions de traitement

Les instructions de traitement (processing instructions) sont conçues pour intégrer des instructions propres à un processeur particulier dans un document XML. Toute instruction apparaît dans une balise de la forme ?nomInstruction attributs . Nous utiliserons par exemple très souvent l’instruction suivante qui indique à un processeur XSLT l’adresse du fichier qui contient le programme pour la transformation du document : <?xml-stylesheet href="prog.xslt" type="text/xslt"> 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.

5

4

5

4

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>

peut être noté
<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 document. 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 document :
<?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. Par exemple le fragment XML suivant n’est pas bien formé :
<A at1=’1’ at1=’2’/>

5

4

5

4

5

5

4

4

60

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Pour ceux qui auraient l’habitude des pratiques assez laxistes de HTML, il est bon de noter que

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 :
<A at1=’1’ at2=’2’/>

est donc identique à :
<A at2=’2’ at1=’1’/>

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 syntaxiques 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 espaces (#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.

5

4

3 3 3 3 3

un attribut doit toujours avoir une valeur, donc la balise HTML du XML bien formé ;

OPTION SELECTED

n’est pas

2.1. LA SYNTAXE XML

61

2.1.8

Sections CDATA

Il peut arriver que l’on souhaite placer dans un document du texte qui ne doit pas être analysé par le parseur. C’est le cas par exemple : 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 :
<![CDATA[Du texte <non interprété> &monTexte;]]>

Cette section est transmise littéralement, et correspond donc à
Du texte <non interprété> &monTexte;

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.

2.1.9

Références d’entités

Chaque référence à une entité doit se faire sous la forme &nom; où nom est le nom de l’entité. En pratique, dans un programme XSLT on considère que le parseur du document a remplacé toutes les références aux entités par leur valeur. Par exemple, la référence
&monTexte;

dans le document ExXML4.xml est remplacée par sa valeur, et on obtient :
texte simple

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 imposé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 &#67; 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 ( , 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 &#60;. À 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 :

8

5

4

GD9 I G F D9 B @ RAQPHCECA9

4

62

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Déclaration entité (prédéfinie) <!ENTITY lt "&#60;"> <!ENTITY gt "&#62;"> <!ENTITY amp "&#38;"> <!ENTITY apos "&#39;"> <!ENTITY quot "&#34;"> Référence &lt; &gt; &amp; &apos; &quot Caractère < > & ’ "

Table 2.1: Entités caractères prédéfinies dans un document XML sans DTD

Du &monTexte; , sans caractères réservés: ni &lt; ni &gt; ni &amp; ni &apos; ni &quot;

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é 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.

2.1.10

La représentation sérialisée d’un document XML est un texte composé de balises (markup) et de données caractères (character data). Les balises permettent de séparer et d’identifier les différentes catégories syntaxiques qui précèdent et définissent les instructions et déclarations utiles pour constituer, structurer, valider et interpréter le contenu « textuel » d’un document XML. Dans XML, tout ce qui ne relève pas l’une des catégories syntaxiques suivantes constitue donc les données caractères d’un document :

3 3 3 3 3 3

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 consé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.

Balises, données caractères et valeur textuelle

la déclaration du document <?xml ....> ; la déclaration de type (si présente) <?DOCTYPE ...> ; les commentaires <!--... les instructions de traitement ; --> ;

2.1. LA SYNTAXE XML

63

Les données caractères représentent toute la partie textuelle d’un document XML qui n’est pas interpré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.

3 3 3

les références d’entités (interne et externe) ; les balises d’éléments (ouvrantes, fermantes et vides) ; les séparateurs des sections CDATA (sans leur contenu) : <![CDATA[ et ]]> : le texte entre ces séparateurs contient des données caractères !

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é introduite dans XSLT.

2.2

Le modèle DOM

Un document XML, quelle que soit sa représentation de départ, correspond à une structure arborescente, et c’est sur cette structure que s’applique le traitement XSLT. Dans la mesure en effet où l’exécution d’un programme XSLT consiste – en simplifiant beaucoup – à se positionner sur des éléments et à produire du texte en « piochant », à partir de l’élément courant, des parties du contenu du document XML, le processeur XSLT doit se baser sur une représentation d’ensemble du document qui supporte les opérations de navigation et d’extraction. Ce serait très difficile sur une représentation sérialisée, c’est plus facile, nous le verrons, sur une représentation arborescente. Comme nous l’avons mentionné au début de ce chapitre, une des normes de description d’une structure XML est le Document Object Model, DOM1 . La modélisation DOM d’un document XML consiste à décrire ce document comme un graphe composé d’objets appartenant à un ensemble déterminé de types de nœuds, la composition des objets étant régie par des règles qui définissent la grammaire de XML. DOM est une spécification de haut niveau, indépendante d’un langage particulier. Une connaissance des concepts orientés-objet de base est donc suffisante. Nous donnons une première présentation des aspects essentiels de cette spécification, avant de la compléter par des exemples.

2.2.1

Types de nœuds DOM

Dans sa représentation DOM, un document XML est essentiellement un arbre constitué de nœuds de différents types. Les types de nœuds sont définis sous forme d’interfaces. Ainsi, un document XML devient un objet qui contient d’autres objets avec des propriétés et méthodes qui peuvent être utilisées pour écrire des applications XML2 . Le tableau 2.2 résume tous les types de nœuds fournis par DOM. Type de Nœuds Document DocumentType ProcessingInstruction Element Attribute Entity EntityReference Comment CharacterData Text CDataSection DocumentFragment Notation Catégorie syntaxique XML document XML (racine) type du document (DTD) instruction de traitement élément XML attribut XML déclaration d’entité référence vers entité commentaire commentaire et section de texte section de texte section CDATA fragment de document XML notation (fait partie de la DTD)

Table 2.2: Types de nœuds DOM
présentation qui suit correspond au DOM (Core) Level 1. Java, DOM utilise la notion d’interface pour souligner l’indépendance d’un langage de programmation et d’une implantation 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 Comme 1 La

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 :

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

Notation

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 :

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 les attributs XML.

3 3 3 3

L’interface DocumentFragment est essentiellement destinée à faciliter la programmation et correspond à 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.

Node

Attribute

TreeNode

Leaf

Container

Character Data

Processing Instruction

Entity

Document

Entity Reference

Element

Document Type

Comment

Text

CData Section

Figure 2.1: Hiérarchie de spécialisation des interfaces 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 ).

66

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION

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.

2.2.2

Interface DOMString

Dans le modèle DOM, comme dans la syntaxe XML, une chaîne est une séquence de caractères Unicode. Ce système d’encodage est largement répandu dans les applications informatiques et aussi utilisé, par exemple, dans le langage Java pour la représentation des chaînes de caractères (le type DOMString peut être directement implanté par le type String de Java). Le standard Unicode définit chaque caractère par un entier sur deux (16-bit Unicode) ou quatre octets (16-bit Unicode étendu). Plus précisément, DOM (et la recommandation XML) distingue entre

les caractères 16-bit Unicode dont les valeurs héxadécimales sont comprises entre #x21 et #xD7FF, et entre #xE000 et #xFFFD ;

les caractères 16-bit Unicode étendu, dont les valeurs héxadécimales sont comprises entre #x10000 et #x10FFFF et entre #xE000 et #xFFFD. Bien que la représentation interne des caractères « étendus » soit deux fois plus longue (4 octets) que la représentation des autres caractères (2 octets), tous sont considérés comme des caractères simples (une position) dans les fonctions qui calculent la taille d’une chaîne de caractères ou la position d’un caractère.

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.

2.2.3

Interfaces NodeList et NamedNodeMap

DOM définit deux interfaces supplémentaires permettant de constituer des groupes de nœuds. Ces deux interfaces sont utilisées pour décrire les résultats de certaines méthodes. Le type NodeList correspond à la notion de tableau indicé. Il représente une liste (ordonnée) de nœuds. Ces nœuds ne sont pas tous forcément de même type. NodeList peut permettre par exemple la manipulation de l’ensemble des fils d’un nœud de l’arbre, parmi lesquels on peut trouver des nœuds de type Text, Element, CDataSection, etc. Le type NamedNodeMap correspond à la notion de tableau associatif (ou table de hachage). Il sert à représenter un ensemble (donc sans ordre) de nœuds identifiés par un nom. Ce type est essentiellement destiné à gérer une liste d’attributs XML.

2.2.4

Interface Node

Le type Node est la racine de toutes les autres interfaces proposées par DOM. Il définit les propriétés essentielles et fournit la plus grande part des opérations importantes dans DOM. Les propriétés spécifiées par l’interface Node sont récapitulées dans le tableau 2.3. Les trois premières permettent de déterminer les informations « locales » concernant un nœud, c’est-à-dire son type, son nom et sa valeur sous forme d’un objet de type DOMString. Le nom et la valeur ne constituent pas des notions pertinentes pour tous les types de nœuds : un nœud de type Text par exemple n’a pas de nom, et un nœud de type Element n’a pas de valeur. On peut remarquer que dans une spécification objet plus rigoureuse l’information sur le nom ou la valeur ne devrait pas apparaître à un niveau global Node mais au niveau d’une interface spécialisée spécifique aux sous-types de Node ayant soit un nom, soit une valeur, soit les deux. DOM n’adopte pas une telle approche, d’une part parce que XML s’y prête assez difficilement, d’autre part pour des raisons

f eVXXXV cV aVXXXV U gbdAYYRYWb`AYYWET

S S S

les caractères blancs : #x9 (tabulation), #xA (saut à la ligne), #xD (retour chariot) et #x20. Généralement, on utilise une représentation héxadécimale qui permet de représenter une valeur par deux sym. Ainsi, les entiers #x9, #xA, #xD et #x20 correspondent boles dans l’ensemble aux entiers 9, 10, 13 et 32 dans le système décimal pour la représentation des entiers ;

2.2. LE MODÈLE DOM
Propriété nodeType nodeName nodeValue parentNode firstChild lastChild childNodes previousSibling nextSibling attributes Type unsigned short DOMString DOMString Node Node Node NodeList Node Node NamedNodeMap

67

Table 2.3: Propriétés du type Node

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 imposé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. Type de nœud CDATASection Comment Document DocumentType Element ProcessingInstruction Text Notation Entity EntityReference Attr nodeName #cdata-section #comment #document nom de la DTD nom de l’élément nom de la cible #text nom de notation (voir page 154) nom de l’entité nom de l’entité référencée nom de l’attribut nodeValue contenu de la section CDATA contenu du commentaire NULL NULL NULL le contenu (moins la cible) contenu du nœud Text NULL NULL NULL valeur de l’attribut

Table 2.4: Interprétation des propriétés nodeName et nodeValue 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

Attr (a) at1 val1

Attr (b) att2 val2

Element (3) B

Element (5) B

Texte (4) texte 1

Comment (6) comment

Texte (7) texte 2

Figure 2.2: Instance d’un arbre DOM 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). Propriétés structurelles de Node La structure arborescente et les liens qui permettent de se déplacer dans cette structure sont représentés par les autres propriétés (sauf attributs) de l’interface Node (tableau 2.3, page 67). Parmi les propriétés structurelles, la plus importante est childNodes qui est de type NodeList et donne la liste des fils d’un nœud de type Container. La structure générale d’un arbre DOM est montrée dans la figure 2.3. Pour chaque type de nœud on indique les enfants possibles par un arc. Tous les arcs sont étiquetés par childNodes[], le symbole [] indiquant que les fils sont stockés sous forme d’une liste ordonnée.

childNodes[] childNodes[]

Leaf

Container

Figure 2.3: Structure générale d’un arbre DOM Voici les autres propriétés : 1. parentNode référence le père d’un nœud ; 2. firstChild et lastChild référencent respectivement le premier et le dernier fils ; 3. previousSibling et nextSibling référencent respectivement le frère gauche et le frère droit 4 . Deux nœuds sont sibling s’ils ont le même père, mais pas s’ils sont au même niveau dans l’arbre mais avec des pères différents. 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 utiliserions volontiers le mot « sœur » si « nœud » était du genre féminin...

2.2. LE MODÈLE DOM

69

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. Propriété parentNode firstChild lastChild childNodes previousSibling nextSibling attributes Nœud 1 2 5 6 5 6 2 4 3 5 2 3 2 4 Valeur NULL 1 6 NULL 7 NULL [3, 5] NULL NULL 3 NULL 5 {a, b} NULL

Table 2.5: Propriétés structurelles pour l’arbre DOM de la figure 2.2

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.

Opérations du type Node 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 cloneNode() 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 Résultat Node Node Node Node boolean Node

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Méthode insertBefore() replaceChild() removeChild() appendChild() hasChildNodes() cloneNode() boolean prof Paramètres Node nouv, Node fils Node nouv, Node anc Node fils Node nouv Explication Insertion du nœud nouv avant le fils fils; retourne nouv Remplacement du fils anc par le nœud nouv; retourne anc Suppression de fils dans la liste des fils; retourne fils Ajout du nœud nouv à la fin de la liste des fils; retourne nouv Retourne vrai si le nœud à des fils et faux sinon Retourne une copie du nœud

Table 2.6: Opérations du type Node

Exemple: parcours d’un arbre DOM Les propriétés et les méthodes de l’interface Node peuvent être utilisées pour créer des programmes. Par exemple, la fonction suivante réalise un parcours d’un arbre DOM en pré-ordre, c’est-à-dire en profondeur à gauche :

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. Appel de fonction parcours_préordre() Paramètre 1 2 3 4 5 6 7 Résultat affiché 1234567 234567 34 4 567 6 7

Table 2.7: Parcours en pré-ordre de l’arbre DOM de la figure 2.2

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

h

fonction parcours_préordre(Node nœud) { print nœud id; print ’ ’; si nœud hasChildNodes() pour tous les nœuds fils dans nœud parcours_préordre(fils); }

h

h

childNodes

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 attributs) 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

Attr (a) at1 val1

Attr (b) att2 val2

Element (5) B

Element (3) B

Texte (7) texte 2

Comment (6) comment

Texte (4) texte 1

Figure 2.4: Instance d’un arbre DOM avec un ordre différent

Maintenant, si on applique la fonction parcours_préordre() aux nœuds de l’arbre de la figure 2.4, on constate que le résultat dans le tableau 2.8 est différent du résultat du parcours de l’arbre d’origine. Appel de fonction parcours_préordre() Paramètre 1 2 3 4 5 6 7 Résultat affiché 1257634 257634 34 4 576 6 7

Table 2.8: Parcours en pré-ordre de l’arbre DOM de la figure 2.4

2.2.5

Interface Document

La structure d’un document XML bien formé doit respecter des contraintes précises. Ainsi, chaque arbre DOM qui correspond à un document XML a une seule racine de type Document qui représente la racine d’un document XML (à distinguer de l’élément racine du document). Dans un arbre DOM, tout accès au contenu d’un document passe donc initialement par ce nœud. Le nœud Document doit obéir aux règles suivantes :

72

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION

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 doctype implementation documentElement Type DocumentType DocumentImpl Element

Le tableau 2.9 donne les propriétés du type Document. La propriété doctype est de type DocumentType et contient la DTD du document XML. Elle est à NULL si la DTD est absente. La propriété implementation 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 : Résultat Element Text Comment CDATASection ProcessingInstruction Attr EntityReference Méthode createElement() createTextNode() createComment() createCDATASection() createProcessingInstruction() createAttribute() createEntityReference() Table 2.10: Opérations du type Document Paramètres DOMString nom DOMString texte DOMString texte DOMString texte DOMString cible, texte DOMString nom DOMString nom

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 particulier, ils ne doivent pas contenir d’espaces.

2.2.6

Un élément XML est un arbre et peut contenir des commentaires, du texte, des références vers les entités et d’autres éléments. De plus, un élément peut avoir zéro, un ou plusieurs attributs. Nous avons déjà souligné qu’un attribut est de type Node mais n’est pas traité de la même façon que les autres nœuds d’un arborescence DOM. Il existe plusieurs raisons à cette distinction :

S S S S S

il a une liste de fils comprenant un nombre quelconque de nœuds de type ProcessingInstruction, Comment et un – un seul – nœud de type Element. Ce nœud est l’élément racine du document ; il peut avoir (ce n’est pas obligatoire) un fils de type DocumentType qui doit apparaître avant l’élément racine et qui correspond à la définition du type du document (voir page 154).

Table 2.9: Propriétés du type Document

Interfaces Element et Attr

les attributs ne peuvent apparaître que comme fils d’un élément ; les attributs d’un élément ne sont pas ordonnés ; un élément ne peut pas avoir plusieurs attributs de même nom.

2.2. LE MODÈLE DOM

73

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

Figure 2.5: Structure d’un élément Les opérations définies pour les éléments sont données dans le tableau 2.11. Résultat DOMString DOMString Méthode getAttribute() setAttribute() Paramètres DOMString name DOMString name, DOMString value DOMString name Attr oldAttr DOMString name Attr newAttr Explication Retourne la valeur de l’attribut name Affecte la valeur value à l’attribut name ; retourne value Supprime l’attribut avec le nom name de la liste d’attributs Supprime l’attribut oldAttr et retourne l’attribut supprimé Retourne la valeur de l’attribut name Insère l’attribut newAttr dans la liste des attributs (si un attribut du même nom existe déjà il est remplacé); retourne l’attribut remplacé ou NULL Retourne la liste des éléments descendants dont le type est égal à name; le résultat est trié dans l’ordre d’un parcours en pré-ordre normalise le contenu textuel d’un élément : un élément normalisé ne contient pas de nœuds de type Text adjacent (tous sont fusionnés en un seul nœud).

void Attr Attr Attr

removeAttribute() removeAttributeNode() getAttributeNode() setAttributeNode()

NodeList

getElementsByTagName()

DOMString name

void

normalise()

Table 2.11: Opérations du type Element 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

2.2.7

Interfaces DocumentType, Entity et EntityReference

La Document Type Definition (DTD) d’un document XML est représentée par un nœud de type DocumentType. Il est optionnel et ne peut apparaître qu’une seule fois dans un arbre DOM, comme propriété du nœud Document. Dans DOM, la seule information qui est accessible à partir de cette interface est la liste d’entités et de notations déclarés dans la DTD. La notion d’entité permet de décomposer un document en plusieurs entités physique qui sont référencées dans le un document. Les interfaces Entity et EntityReference sont surtout importantes pour le passage d’une représentation sérialisée à une représentation DOM. Elles permettent de rassembler des informations distribuées pour créer un nouveau document et représentent précisément la structure physique d’un document XML. Une fois la représentation DOM créée, la structure physique initiale devient peu importante et on peut se concentrer sur la vision logique du document. Nous n’allons pas nous appesantir sur ces notions plus que nécessaire, et considérerons que les références aux entités sont résolues (autrement dit la référence est remplacée par la valeur) au moment de l’application d’un programme XSLT. Ainsi, avec la disparition des entités, la DTD d’un document devient une boîte noire qui ne peut plus être examinée par un programme XSLT.

2.3

Du document sérialisé à l’arbre DOM

Nous allons maintenant comparer la version « sérialisée » d’un document XML avec sa représentation sous forme d’un arbre DOM. Cette comparaison permet de comprendre le passage d’une représentation à l’autre : dans le sens XML – DOM, ce passage correspond à l’analyse d’un document texte par un parseur (processeur XML) qui construit une arborescence DOM pour le traitement du document par un programme. Le sens DOM – XML correspond à la sérialisation d’un arbre DOM, sous forme de document texte. Voici un document XML contenant des éléments avec attributs. Exemple 22 ExXML2DOM1.xml : Fichier XML avec attributs
<?xml version=’1.0’ encoding="ISO-8859-1"?> <A at1="val1"> <B>du texte</B> <C> <D at2="0">autre texte</D> <D at2="1">texte</D> </C> </A>

Le premier attribut est défini dans l’élément A . Son nom est at1 est sa valeur est val1. Cet élément a deux fils B et C 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 &lt; et un &gt;</D> </C> </A>

i p

i p

i

i p

i

2.3. DU DOCUMENT SÉRIALISÉ À L’ARBRE DOM
Document Element A Element B Texte du texte
Attr at1 val1

75

Element C Element D
Attr at2 0 Attr at2 1

Element D

Texte autre texte

Texte texte

Figure 2.6: Représentation DOM du fichier XML 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 &lt; et &gt; on été remplacées par leurs valeurs respectives dans le fils de type Texte de l’élément D.

Document Instruction xml-stylesheet href="feuille.xsl" type="text/xsl" Element A Element C Comment un commentaire
Attr at1 1

Element D Text et un

Figure 2.7: Représentation DOM avec commentaires et instructions de traitement

Au moment de l’analyse, le processeur se base sur des caractères réservés comme , et & pour structurer le document. En contrepartie le contenu textuel ne peut pas contenir de balises, ni même de caractères comme ou puisque ceux-ci vont être interprétés comme faisant partie du marquage. Les sections CDATA permettent de prévenir cette interprétation. Exemple 24 ExXML2DOM3.xml : Document XML avec une section CDATA
<?xml version=’1.0’ ?> <A> &lt;B&gt; <![CDATA[

i p

r

q

un

i

p

76
<?xml version=’1.0’ ?> <A> &lt;B&gt; &lt;/B&gt; </A> ]]> &lt;/B&gt; </A>

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION

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

Figure 2.8: Représentation DOM avec un nœud de type CDATASection

2.3.1

Construction d’un arbre DOM

Nous allons maintenant étudier plus en détail le schéma de construction d’un arbre DOM à partir d’un document XML sérialisé. Voici un fichier XML simple avec quatre éléments : Exemple 25 ExXML2DOMa.xml : Fichier XML
<?xml version=’1.0’ encoding="ISO-8859-1"?> <A> <B>texte simple</B> <C> <D> </D> </C> </A>

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 et 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.

Document fonction parseDoc(Stream fic){ Document document = new Document; appeler parse(fic, document, document); retourner document; }

i

p

r

q

r

r q q

Text B

r

CDATASection ?xml version=’1.0? A &lt;B&gt; &lt;/B&gt; /A

q

Text /B

r q

2.3. DU DOCUMENT SÉRIALISÉ À L’ARBRE DOM

77

fonction parse(Stream fic, Document document, Node père) { Node fils; DOMString fragmentXML; DOMString filsType; boolean finNode = false; tant que le flux fic n’est pas vide et not finNode { fragmentXML = lireToken(fic); si fragmentXML est une balise fermante finNode = true; sinon { filsType = getType(fragmentXML); fils = document create(filsType) ; père appendNode(fils); si filsType est un sous-type de Container appeler parse(fic, document, fils); sinon fils value = extractValue(fragmentXML); } } } Voici quelques explications :

La fonction parseDoc() peut être considérée comme une implantation simplifiée d’un parseur DOM : elle prend comme argument un fichier XML fic et retourne un document de type Document qui est le résultat de l’analyse. Elle crée tout d’abord un nœud de type Document qui est ensuite envoyé comme paramètre à la fonction parse().

La fonction parse() effectue l’analyse du fichier fic passé en paramètre. En plus des paramètres, elle définit quatre variables qui représentent respectivement un fils du nœud à construire (fils), son type (filsType), un fragment du document XML (fragmentXML) à analyser et une variable booléenne qui est égale à true si l’analyse du nœud père est terminée. Ensuite, la fonction entre dans une boucle qui lit successivement le fichier à analyser et s’arrête quand le fichier est consommé ou l’analyse du nœud actuel est terminé. Chaque étape de cette boucle effectue les tâches suivantes : – 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 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

h

h

h

h

S S

78

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Fragment lu <A> Code exécuté b=a createElement(A); a appendNode(b); parse(fic,a,b); c=a createTextNode(#xA#x20); b appendNode(c); d=a createElement(B); g appendNode(d); parse(fic,a,d); e=a createTextNode(texte simple); d appendNode(e); retour (d) f=a createTextNode(#xA#x20); b appendNode(c); g=a createElement(C); b appendNode(d); parse(fic,a,g); h=a createTextNode(#xA#x20#x20); b appendNode(h); i=a createElement(D); g appendNode(i); parse(fic,a,i); j=a createTextNode(#x20); i appendNode(j); retour (i) k=a createTextNode(#xA#x20); g appendNode(k); retour (g) l=a createTextNode(#xA); b appendNode(l); retour (b)

</A>

Table 2.12: Fragmentation du document XML

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.

2.3.2

Traitement des espaces pendant la construction

En étudiant cet arbre DOM on peut observer qu’il existe six nœuds de type Text – identifiés par les entiers 3, 6, 8, 10, 11 et 12 – qui ne contiennent que des espaces (white-space node). Tous ces nœuds (white-space node) sauf le nœud 10 apparaissent comme nœud « séparateurs » entre deux éléments. Le nœud 10 est le seul fils d’un élément de type D. Souvent les espaces entre deux éléments ne sont pas porteurs d’une information significative et servent essentiellement à améliorer la lisibilité d’un document sérialisé par une hiérarchisation du contenu. Aussi, la recommandation XML a décidé de distinguer entre les nœuds-espace significatifs et non-significatifs : cette distinction est seulement possible si le document a une DTD et si on utilise un parseur XML validant qui vérifie la conformité du document par rapport à sa DTD. XSLT permet également de distinguer espaces significatifs et non-significatifs. Néanmoins, les créateurs de XSLT ont décidé que cette séparation ne doit pas dépendre du parseur utilisé avant le traitement

h

</C> #xA

h

</D> #xA#x20

h

#x20

h

<D>

h

#xA#x20#x20

h

<C>

h

</B> #xA#x20

h

texte simple

h

<B>

h

#xA#x20

h

h h h h h h h h h

h

h

2.3. DU DOCUMENT SÉRIALISÉ À L’ARBRE DOM
Document (1) -

79

Element (2) A

Text (3) #xA#x20

Element (4) B

Text (6) #xA#x20 Text (8) #xA#x20#x20

Element (7) C

Text (12) #A Text (11) #xA#x20

Text (5) texte simple

Element (9) D

Text (10) #x20

Figure 2.9: Représentation DOM du fichier XML 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 caractè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.

2.3.3

Deux fonctions de navigation

Pour conclure cette partie sur la syntaxe XML et le modèle DOM, voici deux exemples de fonctions qui permettent d’extraire des éléments dans un document XML. Elles prennent comme argument un nœud DOM et une chaîne de caractères qui correspond au type des éléments fils recherchés.

boolean fonction testNode(Node node, DOMString elType) { retourner (node nodeType == ELEMENT_NODE) et (elType == “*” ou node nodeName == elType); }

NodeList fonction child(Node node, DOMString elType) { Node fils; DocumentFragment résultat = new DocumentFragment; integer i=0; tant que i < node childNodes() length() { fils = node childNodes() item(i); si testNode(fils, elType); résultat = résultat appendChild(fils) i = i+1; } retourner résultat childNodes();

h

h

h

h

h

h

h

h

80 }

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION

La fonction child(node, elType) parcourt tous les fils du nœuds node et vérifie avec testNode() 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 différents nœuds de l’arbre. Appel de fonction child(1,"*") child(1,"A") child(1,"B") child(2,"*") child(2,"B") child(2,"C") child(2,"D") Résultat [2] [2] [] [4,7] [4] [7] []

Table 2.13: Application de la fonction child() à ExXML2DOMa.xml

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.

NodeList fonction descendant(Node node, DOMString elType) { Node fils; DocumentFragment résultat = new DocumentFragment; NodeList descList; integer i=0, j; tant que i < node childNodes() length() { fils = node childNodes() item(i); si testNode(fils, elType) résultat = résultat appendChild(fils) descList = descendant(fils, elType); nombreDesc = descList length(); j = 0; tant que j < descList length() { résultat = résultat appendChild(descListitem(j)); j = j+1; } i = i+1; } retourner résultat childNodes(); } 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.

h

h

h h

h

h

h

h

h

2.4. LE LANGAGE XPATH
Appel de fonction descendant(1,"*") descendant(1,"A") descendant(1,"B") descendant(1,"C") descendant(1,"D") descendant(2,"*") descendant(2,"B") descendant(2,"C") descendant(2,"D") Résultat [2,4,7,9] [2] [4] [7] [9] [4,7,9] [4] [7] []

81

Table 2.14: Application de la fonction descendant() à ExXML2DOMa.xml

2.4

Le langage XPath

Le modèle DOM définit les opérations applicables à un document XML, soit pour en modifier la structure (exemple de la fonction parse()), soit pour « naviguer » au sein de ce document (exemples des fonctions child() et descendant()). Le terme de « navigation » que nous avons utilisé assez intuitivement jusqu’à présent, peut être défini plus précisément comme le référencement, à partir d’un nœud contexte, de un ou plusieurs autres nœuds de l’arbre DOM. Le langage XSLT est fortement fondé sur ce type de référencement. Bien que les opérations applicables aux objets constituant un arbre DOM fournissent un moyen précis et puissant d’effectuer un tel référencement, elles impliquent un recours systématique à la programmation qui s’avèrerait assez lourd. Le langage XPath fournit des expressions beaucoup plus simples et concises pour désigner des nœuds dans un arbre DOM à l’aide de chemins. Ce référencement s’effectue en décrivant les parcours – ou chemins – qui partent du nœud contexte et mènent aux nœuds référencés. Voici quatre exemples d’utilisation de XPath comme langage de référencement dans XSLT : 1. Sélection de nœuds auxquels on souhaite appliquer une règle : dans ce cas l’expression XPath apparaî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). 3. Extraction de valeurs : par exemple l’expression : <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
4. Prédicats de test : voici un exemple rencontré dans le premier chapitre : <xsl:if test=" ($titre = ’’ or TITRE = $titre) and ($seance = ’’ or HEURE &gt;= $seance) and ($ville = ’’ or VILLE = $ville)">

2.4.1

Représentation XPath d’un document XML

XPath s’appuie sur une modélisation DOM restreinte : seuls certains types d’éléments sont considérés, et il n’est pas possible de modifier le document source ou de créer de nouveaux documents. Ces limitations sont compensées – et justifiées – par la grande simplification qui en résulte. Le sous-ensemble des interfaces DOM pris en compte par XPath est mis en évidence dans la figure 2.10 par un fond gris.

Node

Attribute

TreeNode

Leaf

Container

Notation

Character Data

Processing Instruction

Entity

Document

Entity Reference

Element

Document Type

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 soustype 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

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 Du texteCeci est une section littérale avec des ’&’, des ’ ’ et des ’ ’

Figure 2.12: L’arbre traité par XPath, après regroupement

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

r

r

q

q

Text Du texte

CDATASection Ceci est une section littérale avec des ’&’, des ’ ’ et des ’ ’

84

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION

<?xml version="1.0" encoding="ISO-8859-1"?> <?xml-stylesheet href="ex.xsl" type="text/xsl"?> <!-- commentaire --> <!DOCTYPE A SYSTEM "ex.dtd" > <A> <B at1=’ val1 ’ at2=’ valeurs avec blancs ’ /> <C> </C> <D> texte </D> </A>

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 :

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 : <?xml-stylesheet href="ex.xsl" type="text/xsl"?><!-- commentaire --><A> <B at1=’ val1 ’ at2=’ valeurs avec blancs ’/> <C> </C> <D> texte </D> </A> 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 : 1. toutes les fins de lignes sont représentées par le caractère #xA; 2. les valeurs d’attributs sont normalisées et ne contiennent que des caractères blancs (#x20). Les autres caractères (#xA et #xD) ne sont pas éliminés mais remplacés par le #x20. Dans certains cas (quand l’attribut n’est pas de type CDATA – voir section 4.1) les caractères au début et à la fin de la valeur sont effacés. Si, par exemple, l’attribut at1 est de type NMTOKEN (voir page 154), sa valeur devient « val1 ».

S S S S S S

avant la balise ouvrante de l’élément racine ; à l’intérieur d’une balise ; à l’intérieur d’un élément ; à l’intérieur de la valeur d’un attribut ; entre deux balises ; entre deux attributs.

2.4. LE LANGAGE XPATH
Document -

85

Instruction xml-stylesheet href="ex.xsl" type="text/xsl"

Comment un commentaire

Element A

Text #xA#20

Element B

Text #xA#20

Text #xA#20

Element C

Text #xA#20

Element D

Text #xA#20

Attr at1 #20val1#20

Attr at1 #20valeurs#20avec#20blancs#20

Text #20

Text #20texte#20

Figure 2.13: Arbre XPath du document ExBlanc.xml 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) : <?xml-stylesheet href="ex.xsl" type="text/xsl"?><!-- commentaire --> <A><B at1=’val1’ at2=’ valeurs avec blancs ’/><C/><D> texte </D></A> 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 xml-stylesheet href="ex.xsl" type="text/xsl"

Comment un commentaire

Element A

Element B

Comment autre commentaire

Element C

Element D

Attr at1 val1

Attr at1 #20valeurs#20avec#20blancs#20

Text #20texte#20

Figure 2.14: Arbre XPath après suppression des nœuds constitués d’espaces.

2.4.2

Expressions XPath

Le langage XPath désigne un ou plusieurs nœuds en exprimant des chemins dans un arbre conforme à la structure décrite précédemment. L’évaluation d’un chemin donne un résultat qui peut être soit une valeur numérique ou alphanumérique, soit un sous-ensemble des nœuds de l’arbre. Enfin cette évaluation tient compte d’un contexte qui détermine le résultat.
Document -

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Element B

Attr att1 a1

Element D Text Texte1

Element D Text Texte2

Attr att1 a2

Element D Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.15: Exemple d’un arbre XPath

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 : 1. le type DOM du nœud, toujours présent, en gras ; 2. le nom pour les nœuds de type Element (ou la cible pour le type ProcessingInstruction) ; pour tous les autres types de nœud il n’y a pas de nom ;

2.4. LE LANGAGE XPATH

87

3. la valeur, valable pour tous les types de nœuds, y compris les attributs, à l’exception du type Element. 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). Exemple 27 ExArbreXPath.xml : Le fichier XML pour les exemples XPath
<?xml version="1.0" encoding="ISO-8859-1"?> <?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 -

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Element B

Attr att1 a1

Element D Text Texte1

Element D Text Texte2

Attr att1 a2

Element D Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.16: Chemin vers les attributs des nœuds B

88

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION

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 :

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 :

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. Évaluation d’une expression XPath L’évaluation d’une étape peut être soit une valeur, soit un ensemble de nœuds (node-set). Nous laissons de côté, jusqu’à la page 103, le cas des valeurs pour nous concentrer sur le cas où l’évaluation revient à désigner un ensemble de nœuds du document source. Il est bon de noter qu’un nœud n’est jamais extrait du document et placé hors de son contexte (à savoir sa position dans l’arbre). Un node-set doit donc plutôt être considéré comme un ensemble de références vers des nœuds de l’arbre DOM. Notons également qu’un même nœud ne peut être référencé qu’une seule fois dans un même ensemble. Quand une expression est constituée de plusieurs étapes, on se trouve en général dans la situation où l’évaluation de chaque étape donne un ensemble de nœuds. L’évaluation de l’expression complète est alors basée sur les principes suivants :

XX YAX

axe::filtre[prédicat1][prédicat2]

u

[/]étape /étape /.../étape

t

s

2.4. LE LANGAGE XPATH

89

Reprenons un des exemples donnés précédemment pour clarifier cette évaluation : /A/B/@att1 L’expression ci-dessus est un chemin absolu, constitué de trois étapes, dont le nœud contexte initial est donc la racine du document (figure 2.17). L’axe de recherche n’est jamais indiqué, et on suit donc celui par défaut, child, consistant à parcourir les fils du nœud contexte. 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) :

S S S

à partir du nœud contexte, on évalue l’étape 1 ; on obtient un ensemble de nœuds ; on prend alors, un par un, les nœuds de cet ensemble, et on les considère chacun à leur tour comme nœud contexte pour l’évaluation de l’étape 2 ; la règle précédente se généralise comme suit : à chaque étape, on prend successivement comme nœud contexte chacun des nœuds faisant partie du résultat de l’étape précédente.

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Element B

Attr att1 a1

Element D Text Texte1

Element D Text Texte2

Attr att1 a2

Element D Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.17: Le nœud contexte initial

90

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION

Document -

Instruction Java ins1 Element B

Element A
Element B

Comment CommFin Element C

Attr att1 a1

Element D Text Texte1

Element D Text Texte2

Attr att1 a2

Element D Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.18: Première étape de l’évaluation du chemin /A/B/@att1

Document -

Instruction Java ins1

Element A

Comment CommFin Element C

Element B
Element D Text Texte1 Element D Text Texte2

Element B
Element D Text Texte3

Attr att1 a1

Attr att1 a2

Attr att2 a3

Attr att3 15

Figure 2.19: Seconde étape de l’évaluation du chemin /A/B/@att1

2.4. LE LANGAGE XPATH

91

pour tous les fils a dans child(doc, "A") pour tous les fils b dans child(a, "B") pour tous les fils at dans attributes(b, "att1") ajouter at à la liste du résultat Ce programme utilise également une fonction attributes() dont la définition est très similaire à la fonction child(). 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 comportant 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 commun. 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, caracté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érieurement.

2.4.3

Les axes

Nous présentons maintenant de manière exhaustive les options des trois composants d’une étape dans un chemin XPath (axes, filtres et prédicats), en commençant par les axes. Rappelons que tout axe s’interprète par rapport à un nœud contexte. Un axe XPath recouvre alors les deux notions suivantes : 1. un sous-ensemble des nœuds de l’arbre relatif au nœud contexte ; 2. l’ordre de parcours de ces nœuds à partir du nœud contexte. Le tableau 2.15 donne la liste des axes XPath. Tous ces axes désignent des nœuds de l’arbre DOM, à l’exception de l’axe attribute qui s’applique à l’arbre des attributs et namespace qui s’applique aux espaces de nom, ce dernier aspect étant laissé de côté jusqu’au chapitre 6. Il est toujours possible de spécifier une étape XPath en donnant pour axe un des noms de la première colonne du tableau 2.15. Certaines facilités d’écriture sont cependant permises :

Nous décrivons maintenant chaque axe, en prenant comme exemple de référence l’arbre de la figure 2.20, avec comme nœud contexte le second nœud de type B, comme illustré sur la figure.

S S

quand l’axe n’est pas indiqué dans une étape, c’est child qui est pris par défaut ; certains axes peuvent être notés de manière abrégée, comme par exemple « @ » qui remplace attribute.

92 Axe child attribute parent descendant ancestor self preceding-sibling following-sibling preceding following descendant-or-self ancestor-or-self namespace

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Description Les fils du nœud contexte Les attributs du nœud contexte Le père du nœud contexte Tous les descendants du nœud contexte Tous les ancêtres du nœud contexte Le nœud contexte lui-même Tous les frères gauches du nœud contexte Tous les frères droits du nœud contexte Les nœuds précédant le nœud contexte dans l’ordre de parcours du document Les nœuds suivant le nœud contexte dans l’ordre de parcours du document Les descendants du nœud contexte, et le nœud contexte lui-même Les ancêtres du nœud contexte, et le nœud contexte lui-même S’applique aux espace de noms : voir chapitre 6 Table 2.15: Les axes XPath
Document -

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Element B
Element D Text Texte2 Element D Text Texte3

Attr att1 a1

Element D Text Texte1

Attr att1 a2

Attr att2 a3

Attr att3 15

Figure 2.20: Exemple de référence pour les axes XPath 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. L’axe peut être utilisé explicitement : /child::A/child::B/child::D Mais le plus souvent on omettra l’indication child pour se reposer sur l’interprétation par défaut. L’expression ci-dessous est donc équivalente à la précédente : /A/B/D Notons que l’évaluation de l’expression ci-dessus, appliquée à notre arbre-exemple, donne tous les nœuds de type D.

2.4. LE LANGAGE XPATH
Document -

93

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Nœud contexte

Attr att1 a1

Element D Text Texte1

Element D Text Texte2

Attr att1 a2

Element D
Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.21: L’axe child 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 -

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Nœud contexte

Attr att1 a1

Element D Text Texte1

Element D Text Texte2

Attr att1 a2

Element D Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.22: Résultat de attribute::att1

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 disparition 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émentracine 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 -

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Nœud contexte

Attr att1 a1

Element D Text Texte1

Element D Text Texte2

Attr att1 a2

Element D Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.23: Résultat de parent::A

L’abréviation pour cet axe est « .. », conformément à une convention largement utilisée dans des systè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 attributs). 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
L’axe descendant

95

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 -

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Nœud contexte

Attr att1 a1

Element D Text Texte1

Element D Text Texte2

Attr att1 a2

Element D Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.24: Résultat de descendant::node()

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 -

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Nœud contexte

Attr att1 a1

Element D Text Texte1

Element D Text Texte2

Attr att1 a2

Element D Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.25: Résultat ancestor::node() 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 Les axes preceding-sibling et following-sibling

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 Les axes preceding et following Ces deux axes désignent respectivement l’ensemble des nœuds qui précèdent ou qui suivent le nœud contexte dans l’ordre de parcours du document. Rappelons que cet ordre correspond, pour la représentation arborescente, à un parcours gauche en profondeur d’abord, et plus simplement, pour la représentation sérialisée, à un parcours séquentiel des nœuds.

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 -

Instruction Java ins1

Element A

Comment CommFin Element C

Element B
Element D Text Texte1 Element D Text Texte2

Nœud contexte

Attr att1 a1

Attr att1 a2

Element D Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.26: Résultat de preceding-sibling::node()

Document -

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Nœud contexte

Attr att1 a1

Element D Text Texte1

Element D Text Texte2

Attr att1 a2

Element D Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.27: Résultat de following::node()

98

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION

Les axes descendant-or-self et ancestor-or-self Comme l’indiquent leurs noms, ces axes désignent respectivement l’union des axes descendant et self, et l’union des axes ancestor et self, Par exemple l’expression suivante : 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.

2.4.4

Les filtres

Un filtre permet d’éliminer un certain nombre de nœuds parmi ceux sélectionnés par un axe. Il existe essentiellement deux façons de filtrer les nœuds : 1. par leur nom ; 2. par leur type DOM. Filtrage par nom Le filtrage par nom ne s’applique qu’aux nœuds pour lesquels cette notion de nom est pertinente, à savoir ceux de type Element, de type ProcessingInstruction et de type Attr. Il s’effectue simplement en indiquant le nom, ou une partie du nom. La plus grande part des exemples d’expression XPath que nous avons vus jusqu’à présent comprenaient un filtrage par nom. Par exemple l’expression suivante : /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 -

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Element B

Attr att1 a1

Element D
Text Texte1

Element D
Text Texte2

Attr att1 a2

Element D
Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.28: Filtre avec noms d’éléments (/A/B/D)

2.4. LE LANGAGE XPATH
Document -

99

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Element B

Attr att1 a1

Element D Text Texte1

Element D Text Texte2

Attr att1 a2

Element D Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.29: Filtre avec noms d’attributs (/descendant::node()/@att2) 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 Le résultat est montré dans la figure 2.29. Il est possible de désigner tous les nœuds de type Attr (pour l’axe attribute) ou de type Element (pour tous les autres axes) avec le caractère « * ». L’expression suivante va donc sélectionner tous les attributs de l’arbre, quel que soit leur nom : /descendant::node()/@*

/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).

Filtre sur le type de nœud Les filtres par nommage ne concernent que les attributs ou les nœuds de type Element. Des filtres spécifiques existent donc pour les autres types, à savoir Text, Comment et ProcessingInstruction. Le tableau 2.16 montre les filtres sur les types de nœuds. Voici quelques exemples pour illustrer l’utilisation de ces filtres. L’expression /processing-instruction() désigne les nœuds fils de la racine du document de type ProcessingInstruction. En prenant notre exemple on obtient le premier fils de la racine du document. De même, pour désigner le troisième fils, de type Comment, on utiliserait :

i p

De même l’expression suivante sélectionne tous les éléments fils de l’élément racine soit leur nom :

A , quel que

100

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Document -

Instruction Java ins1

Element A

Comment CommFin

Element B
Element D Text Texte1 Element D Text Texte2

Element B
Element D Text Texte3

Element C

Attr att1 a1

Attr att1 a2

Attr att2 a3

Attr att3 15

Figure 2.30: Résultat de /A/* Filtre text() comment() processing-instruction() node() Description Désigne les nœuds de type Text Désigne les nœuds de type Comment Désigne les nœuds de type ProcessingInstruction Désigne tous les types de nœud

Table 2.16: Les filtres sur 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 B /A/B//text() Notez l’utilisation de la notation abrégée « // ». En notation non abrégée on aurait : /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é.

i p

2.4. LE LANGAGE XPATH
Document -

101

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Element B

Attr att1 a1

Element D

Element D

Attr att1 a2

Element D

Attr att2 a3

Attr att3 15

Text Texte1

Text Texte2

Text Texte3

Figure 2.31: Résultat de /A/B//text() 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 : 1. l’ordre des nœuds du document XML ; 2. l’axe utilisé dans l’étape XPath : la plupart des axes respectent l’ordre du document (forward-axes), mais d’autres, dits reverse-axes, adoptent l’ordre inverse. La règle générale est que tous les axes qui peuvent désigner des nœuds situés avant le nœud contexte (ancestor, ancestor-or-self, preceding-sibling, et preceding) sont des reverse-axes.

102

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Document -

Instruction Java ins1 Element B

Element A

Comment CommFin Element C

Element B

Attr att1 a1

Element D

Element D Text Texte2

Attr att1 a2

Element D

Attr att2 a3

Attr att3 15

Text Texte1

Text Texte3

Figure 2.32: Résultat de /A/B/descendant::text()[position()=1] Il est possible de tester les valeurs de certains nœuds en faisant référence à ces nœuds par des expressions 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. /A/B[@att1=’a1’ and position()=last()] Composition de prédicats L’utilisation d’une succession de prédicats marqués par les crochets [ ] dénote une composition. La diffé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. /A/B[@att1=’a1’ and position()=last()] 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.

2.4.6

Types et opérations XPath

XPath fournit un support limité pour effectuer des opérations ou des comparaisons sur des nombres ou des chaînes de caractères. Ces opérations ou comparaisons impliquent le plus souvent un typage de certaines parties d’un document XML. On peut les utiliser pour les prédicats, afin de tester des valeurs numériques par exemple, mais aussi avec XSLT quand on chercher à produire un résultat calculé à partir des informations du document source. On peut vouloir par exemple prendre des sous-chaînes, effectuer des calculs arithmétiques, etc. XPath connaît quatre types de données : 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 document, et délimitées par des guillemets (") ou des apostrophes (’) : attention les blancs sont significatifs (“azerty” est différent de “ azerty “) ; 3. les booléens correspondent aux valeurs habituelles true et false ; 4. enfin les ensembles de nœuds sont un sous-ensemble, sans doublon, des nœuds du document source. , ) peuvent être utilisés pour tous les types scalaires Les comparateurs usuels ( , , !=, =, (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. La compréhension des principales règles de typage, d’évaluation d’expressions typées et de conversions d’un type à un autre ci-dessous est donc essentielle. Nous ne donnons pas en revanche dans ce chapitre la liste complète des fonctions XPath qui figure dans l’annexe B

v v Ci Cp

i p

104 Opérateurs + -

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION
Syntaxe et description Addition : n1 + n2 Soustraction : n1 - n2. Attention à laisser un blanc de chaque côté du ’-’, sinon le signe est interprété comme faisant partie de n1 ou n2. Multiplication : n1 * n2 Division : n1 div n2 (laisser un blanc de chaque côté) Reste de la division : n1 mod n2 (laisser un blanc de chaque côté) Table 2.17: Opérateurs numériques

div mod

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 :

élimination des blancs avant le premier caractère et après le dernier caractère ;

recherche du signe ’-’ ;

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 :

w

S S S

Instruction Java ins1 Element B

Element A

Comment CommFin

Element B

Element C

Attr att1 a1

Element D Text Texte1

Element D Text Texte2

Attr att1 a2

Element D Text Texte3

Attr att2 a3

Attr att3 15

Figure 2.33: Résultat de //node()[number(@att1) mod 2 = 1]

2.4. LE LANGAGE XPATH
1. NaN = NaN vaut false ; 2. NaN != NaN vaut true ; 3. number(X) != number(X) vaut true si X n’est pas un nombre !

105

Nous voici donc dotés d’un bon moyen de rendre des expressions incompréhensibles. Ce n’est pas le dernier. 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 :

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 ; 5 : on convertit le booléen true en numérique, ce qui donne 1, et le résultat de la 2. true() 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() ’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 ;

i

i

S S

Vers un numérique (fonction number()) : on obtient 1 avec true, et 0 avec false. Vers une chaîne (fonction string()) : on obtient la chaîne false ou la chaîne true.

106 Ensembles de nœuds

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION

É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 :

pour un nœud de type ProcessingInstruction, on obtient toute la chaîne comprise entre le nom (ou « cible ») de l’instruction et ’? ’ ; pour un nœud de type Attr, on obtient la valeur de l’attribut ;

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.
De/Vers Booléen Numérique Booléen – O et NaN false ; sinon true si vide false ; sinon true si vide false ; sinon true Numérique false true 1 – 0; Chaîne false ’false’ ; true ’true’ format décimal Ensemble de nœuds Interdit Interdit

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é : 1. pour comparer et une chaîne , on convertit tous les nœuds de vers leurs valeurs textuelles ; si une seule de ces valeurs est égale à la comparaison renvoie true, et false sinon ; 2. pour comparer et un nombre , on effectue comme précédemment une conversion de chaque nœuds vers une valeur numérique, et on compare avec ;
5 Cette

expression est équivalent à /A/B[position() = 1].

y

y



€



€

x

Ensemble de nœuds

x

Chaîne

conversion vers nombre conversion chaîne puis nombre

– contenu du premier nœud dans l’ordre du document

Interdit –

Table 2.18: Conversions de types XPath

i

p

x

x

x

x

x

i

y

y

S S S S S

pour un nœud de type Text, on obtient directement le contenu du nœud ; pour un nœud de type Comment, on obtient toute la chaîne comprise entre ’ !--’ et ’-- ’ ;

2.4. LE LANGAGE XPATH

107 vers un

4. enfin pour comparer avec un second ensemble , on prend les valeurs textuelles des nœuds de et de et on les compare deux à deux : si on obtient true pour au moins une paire, alors la comparaison globale renvoie true également.

2.4.7

Exemples d’expressions XPath

Nous concluons ce chapitre par des exemples (commentés !) d’expression, en abandonnant à partir de maintenant l’exemple de référence que nous avons utilisé tout au long de cette section afin de pouvoir explorer des cas plus riches et généraux. Toutes les expressions qui suivent sont données d’abord dans leur forme développée puis, le cas échéant, en utilisant la forme abrégée.

child::A/descendant::B : donne les éléments de type B descendants d’un élément de type A, lui-même fils du nœud contexte ; Forme abrégée : A//B

child::*/child::B : donne les éléments de type B petit-fils du nœud contexte ; Forme abrégée : */B

descendant-or-self::B : donne les éléments de type B fils du nœud contexte, et le nœud contexte lui-même s’il est de type B ;

child::B[position()=last() - 1] : donne l’avant-dernier élément de type B fils du nœud contexte ; Forme abrégée : B[last()-1]

following-sibling::B[position()=1]: donne le premier frère de droite du nœud contexte dont le type est B ;

/descendant::B[position()=12] : donne le douzième élément de type B du document ;

child::B[child::C] : donne les fils de type B qui ont eux-mêmes au moins un fils de type C : le prédicat est ici une expression XPath qui renvoie un ensemble de nœuds, interprété comme false s’il est vide, et true sinon ; Forme abrégée : B[C]

/descendant::B[attribute::att1 and attribute::att2] : donne les éléments de type B qui ont au moins un attribut att1 et un attribut att2 ; Forme abrégée : //B[@att1 and @att2]

child::*[self::B or self::C] : donne les fils de type B ou de type C ;

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.

y

3. pour une comparaison avec une valeur booléenne, on prend simplement la conversion de booléen ;

t ‚y

y

t ‚y

y

S S S S S S S S S

108

CHAPTER 2. DOCUMENTS XML : STRUCTURE ET NAVIGATION

Abréviation . .. // .// @att

Description Équivalent à self::node() Équivalent à parent::node() Équivalent à /descendant-or-self::node()/ Équivalent à self::node()/descendant-or-self::node()/ Équivalent à attribute::att Table 2.19: Les abréviations XPath

Chapter 3

XSLT
Sommaire
3.1 Programmes XSLT . . . . . . . . . . . . . . . . . . . . . . . . . 3.1.1 Structure d’un programme XSLT . . . . . . . . . . . . . . 3.1.2 Modularité : xsl:import et xsl:include . . . . . . 3.1.3 Application d’un programme XSLT . . . . . . . . . . . . . Les règles XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.1 Les patterns . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.2 Règles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.3 Déclenchement de règles avec xsl:apply-templates 3.2.4 Sélection des règles . . . . . . . . . . . . . . . . . . . . . 3.2.5 Appel de règle avec xsl:call-template . . . . . . . 3.2.6 Paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . Instructions de contrôle . . . . . . . . . . . . . . . . . . . . . . 3.3.1 Tests : xsl:if et xsl:choose . . . . . . . . . . . . . . 3.3.2 Boucles : xsl:for-each . . . . . . . . . . . . . . . . . 3.3.3 Variables : xsl:variable . . . . . . . . . . . . . . . . 3.3.4 Tri : xsl:sort . . . . . . . . . . . . . . . . . . . . . . . 3.3.5 Itérations par récursion . . . . . . . . . . . . . . . . . . . Évaluation d’un programme XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 111 115 117 119 119 122 124 126 131 132 134 134 136 138 140 141 146

3.2

3.3

3.4

Dans ce chapitre nous commençons notre étude approfondie de XSLT par les aspects relatifs à la programmation. Nous rassemblons sous ce terme toutes les instructions qui permettent de spécifier une transformation 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 : 1. nous ne nous intéressons pas au style de programmation XSLT ; 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

arbre DOM Analyse Document XML

Processeur XSLT

arbre résultat Sérialisation

Document XSLT

Document résultat

Figure 3.1: Schéma du traitement d’une transformations XSLT

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 processeurs 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 produit 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 transformation 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. Exemple 28 CoursXML.xml : Document XML pour le cours associé à ce livre
<?xml version=’1.0’ encoding="ISO-8859-1"?> <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.

3.1

Un programme XSLT est un document XML. Ce document doit bien entendu être bien formé (fermeture des balises, non-imbrication des balises ouvrante et fermante, etc). Les instructions spécifiques à XSLT doivent aussi respecter une certaine structure – relativement simple – et se limiter à des types d’éléments prédéfinis.

ƒ ƒ

le document XML source le programme XSLT

Programmes XSLT

3.1. PROGRAMMES XSLT

111

3.1.1

Structure d’un programme XSLT

Un programme XSLT doit respecter les règles syntaxiques XML. Il faut bien être conscient que cela s’applique non seulement aux éléments XSLT eux-mêmes, mais également aux éléments qui sont destinés à être insérés dans le résultat, que nous appellerons élément littéraux par la suite. Le document XSLT suivant par exemple n’est pas bien formé. Exemple 29 ExXSLT1.xsl : Un document XSLT mal formé
<?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="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 p et hr 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 On trouve donc en général deux attributs :

CHAPTER 3. XSLT

1. L’attribut version est obligatoire et correspond au numéro de version de la « recommandation » XSLT publiée par le W3C. La seule recommandation à l’heure où ce livre est écrit est la 1.0, la 1.1 étant à l’état de Working Draft. 2. L’attribut xlmns:xsl définit l’espace de nom (namespace) xsl et l’associe à l’URI http://www.w3.org/1999/XSL/Transform Ce mécanisme permet de qualifier tous les éléments XSLT par le préfixe xsl:, et de considérer tous les autres éléments comme des éléments littéraux à inclure dans le document résultat. Les espaces de nom sont étudiés dans le chapitre 6. 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 exhaustive 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 distingue communément deux types d’éléments. Tout d’abord les éléments de premier niveau (top-level elements) 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. Éléments de premier niveau Les types d’élément qui peuvent être fils de l’élément racine xsl:stylesheet sont dit « éléments de premier niveau » (top-level éléments). Ils sont tous (du moins ceux de la version 1.0) rassemblés dans le tableau 3.1, avec une courte description et la page où vous pouvez trouver la référence pour l’élément. Type d’élément xsl:attribute-set xsl:decimal-format xsl:import xsl:include xsl:key xsl:namespace-alias xsl:output xsl:param xsl:preserve-space xsl:strip-space xsl:template xsl:variable Description Définit un groupe d’attributs (page 259) Définit un format d’affichage pour les numériques (page 266) Import d’un programme XSLT (page 268) Inclusion d’un programme XSLT (page 269) Définit une clé pour un groupe de nœuds (page 270) Définit des alias pour certains espaces de nom (page 271) Indique le format de sortie (HTML, XML, texte) (page 273) Définit un paramètre (page 273) Conserve les nœuds texte constitués d’espaces pour certain éléments (page 274) Supprime les nœuds texte constitués d’espaces pour certains éléments (page 278) Définit une règle XSLT (page 279) Définit une variable XSLT (page 281) Table 3.1: Éléments XSLT de premier niveau 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

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 h3 , ol , li et leurs fermetures), des nœuds de type Text (par exemple le nœud fils de h3 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.

…

„ …

„ …

„

…

„

ƒ

les éléments qui s’appliquent à la transformation du document source.

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. Type d’élément xsl:apply-imports xsl:apply-templates xsl:attribute xsl:call-template xsl:choose xsl:comment xsl:copy xsl:copy-of xsl:document xsl:element xsl:fallback xsl:for-each xsl:if xsl:message xsl:number xsl:variable xsl:processing-instruction xsl:text xsl:value-of xsl:variable Description Permet d’appliquer une règle importée, tout en la complétant par un nouveau corps (page 255) Déclenche l’application de règles (page 256) Insère un attribut dans un élément du document résultat (page 258) Appelle une règle par son nom (page 259) Structure de test équivalente au switch d’un langage comme Java ou C++ (page 261) Insère un nœud Comment dans le document résultat (page 262) Copie un nœud du document source dans le document résultat (page 264) Copie un nœud, ainsi que tous ses descendants (page 265) Permet de créer plusieurs documents résultats (XSLT 1.1) (page 266) Insère un nœud Element dans le document résultat (page 267) Règle déclenchée si le processeur ne reconnaît une instruction (page 268) Pour effectuer des itérations (page 268) Pour effectuer un branchement conditionnel (page 268) Pour produire un message pendant le traitement XSLT (page 270) Permet de numéroter les nœuds du document résultat (page 272) Permet de définir un paramètre (page 273) Insère un nœud ProcessingInstruction dans le document résultat (page 275) Insère un nœud Text dans le document résultat (page 280) Évalue une expression XPath et insère le résultat (page 280) Permet de définir une variable (page 281)

Table 3.2: Instructions XSLT

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 relatives à la transformation du document source plutôt qu’à la production du résultat sont xsl:applytemplates et xsl:call-template qui toutes deux déclenchent des règles, xsl:choose, xsl:foreach, 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

3.1.2

Modularité : xsl:import et xsl:include

Jusqu’à présent nous avons toujours considéré qu’un programme XSLT était contenu dans un seul fichier. Heureusement il est possible, comme dans tout langage de programmation digne de ce nom, d’introduire une certaine modularité afin de répartir les instructions dans plusieurs fichiers d’un taille raisonnable, et surtout de pouvoir réutiliser une règle dans plusieurs programmes. XSLT fournit deux éléments de premier niveau pour intégrer des fichiers afin de constituer des programmes : ce sont xsl:import et xsl:include. Signalons tout de suite que les notions de « librairie » constituée de « module » qui ne peuvent être utilisés indépendamment, et de « programme principal » constitué par assemblage de librairies, n’existent pas en XSLT. Rien ne distingue, du point de vue syntaxique, un programme destiné à être inclus d’un programme incluant ou important d’autres fichiers. Tous doivent être des programmes XSLT conformes, avec un élément racine xsl:stylesheet. L’assemblage de plusieurs programmes peut créer des conflits. Au moment de l’évaluation plusieurs règles peuvent s’appliquer aux mêmes nœuds. Afin de déterminer la règle à appliquer, XSLT utilise un système de priorités que nous décrivons page 127. La distinction entre xsl:include et xsl:import est justement relative à la méthode appliquée pour choisir une règle en cas de conflit : 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 distinguer, 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 TITRE , et enfin déclenche un appel de règles avec xsl:apply-templates. Exemple 30 ExXSLTImport.xsl : Un programme à importer
<?xml version="1.0" encoding="ISO-8859-1"?> <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 priorité d’application des règles.

…

„

116
</body> </html> </xsl:template> <xsl:template match="SUJET | ENSEIGNANTS | PROGRAMME"/> </xsl:stylesheet>

CHAPTER 3. XSLT

La seconde règle s’applique aux fils de COURS 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 programme 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 suivante :

‰ g†

‰ †

si un document est importé avant un document supérieure à celles de ;

…

„

‡ ˆ†

‡ ˆ†

ƒ

, alors toutes les règles de

ont une préséance

3.1. PROGRAMMES XSLT

117

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.
A

B C D

E F

Figure 3.2: Exemple de plusieurs imports 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 correspond à 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 programme 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.

3.1.3

Application d’un programme XSLT

Un programme XSLT s’applique à un document XML pour en effectuer une transformation. Comment et quand déclencher cette transformation ? Si on prend comme application de base la publication d’informations sur le Web, on peut envisager plusieurs situations : 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.

‡ ”‘

“ E‘

‰ A‘

“‘ ‡ ’‘

ƒ

si une règle supérieure à

a une préséance supérieure à (transitivité).

, elle-même supérieure à

, alors

a une préséance

118

CHAPTER 3. XSLT

La plupart des processeurs permettent des transformations « statiques » à partir de la ligne de commande. 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. java org.apache.xalan.xslt.Process -in Alien.xml -xsl Film.xsl -out 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 documents 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 compatibilité 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,

Le moyen le plus courant pour associer un document XML à un programme XSLT est une instruction de traitement ?xml-stylesheet? 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. <?xml-stylesheet href="Film.xsl" type="text/xsl"?> C’est une instruction de traitement, dont le nom est xml-stylesheet, et le contenu une liste d’attributs2 . Ces attributs sont :

href, qui donne l’URI du programme XSLT (ce peut être un fichier local, ou un fichier accessible sur le Web) ;

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 ;

title, une chaîne de caractère qui peut être utilisée pour permettre le choix du programme à appliquer ;

media, qui indique le format du document produit par la transformation ;

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 ?xml-stylesheet? 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. <?xml-stylesheet href="Film.xsl" type="text/xsl"?> <?xml-stylesheet href="FilmWML.xsl" type="text/xsl" media="wap"?>
instruction de traitement n’étant pas un élément, on ne peut pas dire qu’elle a des « attributs » au sens strict du terme. Il ne s’agit ici que d’un choix de présentation afin de clarifier le contenu de l’instruction, mais celui-ci pourrait être constitué de texte non structuré.
2 Une

…

…

…

„

„

L’instruction

?xml-stylesheet?

„

ƒ ƒ ƒ ƒ ƒ

3.2. LES RÈGLES XSLT

119

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 processeur définit ses propres modes d’application de programme XSLT. Signalons également que la recommandation 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.

3.2

Les règles XSLT

Nous décrivons dans cette section les règles XSLT et leur déclenchement. Il s’agit d’un des aspects essentiels de XLST puisque dans beaucoup de cas on peut écrire un programme sous la forme de règles se déclenchant sur certains nœuds et produisant une partie du résultat. La définition d’une règle s’effectue avec l’élément xsl:template, et le déclenchement avec xsl:apply-templates ou xsl:calltemplate. Une règle peut être déclenchée (« instanciée ») soit par son nom, soit en donnant la catégorie des nœuds du document source auxquels elle s’applique. Cette catégorie est spécifiée par une sous-classe des expressions XPath désignée par le terme pattern dans la recommandation XSLT. Nous commençons par présenter ces patterns avant de revenir en détail sur les règles et leur instanciation.

3.2.1

Les patterns

Le déclenchement des règles basé sur les patterns n’est pas la partie la plus évidente de XSLT. Nous prenons un exemple simple pour commencer. Un exemple Le programme suivant recherche et affiche les noms des enseignants, les attributs ID et l’intitulé des séances de cours. Exemple 33 Pattern1.xsl : Exemple pour illustrer les patterns
<?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="//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
</xsl:template> </xsl:stylesheet>

CHAPTER 3. XSLT

Appliqué à notre document CoursXML.xml (page 110), on obtient le document suivant : Exemple 34 Pattern1.xml : Le résultat du programme
<?xml version="1.0" encoding="ISO-8859-1"?> <NOM>Amann</NOM><NOM>Rigaux</NOM> <IDSEANCE>1</IDSEANCE> <IDSEANCE>2</IDSEANCE> <IDSEANCE>3</IDSEANCE> <SEANCE>Introduction</SEANCE> <SEANCE>Documents XML</SEANCE> <SEANCE>Programmation XSLT</SEANCE>

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 : 1. l’expression dans l’élément xsl:apply-templates sert à désigner un ensemble de nœuds, et constitue donc une utilisation classique de XPath ; 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. Les deux éléments fonctionnent solidairement : xsl:apply-templates permet de constituer un ensemble de nœuds issus du document source. Puis, pour chacun de ces nœuds, le processeur XSLT va chercher la règle (on suppose qu’il n’y en a qu’une pour l’instant) telle que le nœud « satisfait » la condition exprimée par l’attribut match. Il reste à définir la satisfaction d’un condition exprimée par l’expression de l’attribut match – appelons cette expression pattern à partir de maintenant. On peut l’exprimer assez simplement de la manière suivante : un nœud satisfait un pattern s’il existe quelque part dans l’arbre du document source un nœud tel qu’en évaluant le pattern avec comme nœud contexte, on obtienne un ensemble qui contient . 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. La règle avec match="NOM" Le premier xsl:apply-templates constitue, par évaluation de l’expression XPath, l’ensemble de tous les nœuds de type NOM. Pour chacun de ces nœuds, le processeur XSLT va chercher la (ou les) règles dont le nœud satisfait le pattern. Seule la seconde règle convient. En prenant en effet comme nœud contexte le nœud de type ENSEIGNANT et en évaluant le pattern NOM, j’obtiens bien tous les nœuds de type NOM. Remarquez que cette règle se déclenche pour tous les éléments de type NOM, quelle que soit leur position dans l’arbre, puisqu’il suffit de prendre le père de ces nœuds (quel qu’il soit) et d’évaluer le pattern pour que la condition soit satisfaite.

–

•

–

•

3.2. LES RÈGLES XSLT
La règle avec match="@ID"

121

Le second xsl:apply-templates constitue, par évaluation de l’expression XPath, l’ensemble de tous les attributs ID des nœuds de type SEANCE. Cette fois c’est la troisième règle qui est déclenchée. En prenant en effet le père SEANCE de chaque attribut et en évaluant le pattern @ID, on obtient bien l’attribut lui-même. Comme précédemment, remarquez que cette règle se déclenche pour tous les attributs ID, quel que soit le type de l’élément auquel ils appartiennent. La règle avec match="PROGRAMME/SEANCE" Le dernier xsl:apply-templates constitue, par évaluation de l’expression XPath, l’ensemble de tous nœuds de type SEANCE. C’est la dernière règle qui va être déclenchée. Prenons en effet le premier nœud SEANCE comme nœud courant. En prenant le père de ce nœud ( PROGRAMME ) comme nœud contexte et en évaluant le pattern, on obtient un ensemble vide. On ne peut donc pas dire, à ce stade, que la condition est satisfaite. En prenant maintenant le grand-père, le nœud COURS , et en évaluant PROGRAMME/SEANCE, on obtient bien un ensemble qui contient notre premier nœud SEANCE , et la condition déclenchant la règle est satisfaite. On peut noter cette fois que la règle ne s’applique qu’aux éléments de type SEANCE, eux-même fils d’un élément de type PROGRAMME. Cette règle est donc moins générale. En conclusion un pattern dans une règle définit la « situation » des nœuds de l’arbre qui vont pouvoir déclencher la règle. Cette situation peut être très générale : un pattern « NOM » définit une règle applicable à tous les éléments de type NOM, quel que soit leur position, et que que soit le type de leurs parents. Un pattern comme « PROGRAMME/SEANCE » est déjà plus restrictif. À l’extrême, un pattern peut être un chemin absolu XPath, et la règle ne s’applique plus alors qu’à des arbres très particuliers. Expressions XPath et patterns Pourquoi distinguer les patterns et les expressions XPath ? Pour les deux raisons suivantes : 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. <!-- Pas bon <xsl:template match="1"> ... </xsl:template> -->

De plus, toutes les expressions XPath donnant un ensemble de nœuds ne sont pas acceptées, essentiellement pour des raisons de performance et de complexité d’implantation du processeur XSLT. La règle suivante par exemple est interdite, et ce bien que l’attribut match soit une expression XPath dont l’évaluation donne un ensemble de nœuds. <!-- Pas bon --> <xsl:template match="preceding::node()[5]"> ... </xsl:template>

…

„

…

…

„

„

…

„

…

„

122

CHAPTER 3. XSLT

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 par rapport à un nœud courant selon l’algorithme suivant :

É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.

/COURS/ENSEIGNANTS sera satisfait par tous les nœuds de type ENSEIGNANTS fils d’un élément racine de type COURS ;

//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) ;

NOM[position()=2] sera satisfait par tout nœud qui est le deuxième fils de type NOM de son père ;

*[position()=2][name()="NOM" sera satisfait par tout nœud de type NOM, second fils de son père ;

/COURS/@CODE[.="TC234"] sera satisfait par le nœud de type Attr, de nom CODE, fils de l’élément racine COURS , 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 attributs 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 effectuer 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. sinon recommencer en parcourant les ancêtres de

jusqu’à la racine du document.

•

•

…

•

2. sinon prendre le père de ;

, et recommencer l’évaluation ; si

fait partie du résultat, alors

satisfait

—

•

•

—

„

•

1. prendre

comme nœud contexte et évaluer

; si

fait partie du résultat, alors

satisfait

•

•

•

—

•

;

—

ƒ ƒ ƒ ƒ ƒ

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:calltemplate ; 3. mode permet de définir des catégories de règles, à appeler dans des circonstances particulières ; 4. enfin priority donne une priorité explicite à la règle. 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. Règles par défaut XSLT définit un ensemble de règles par défaut qui sont appliquées quand aucune règle du programme n’est sélectionnée. La première règle par défaut s’applique à la racine du document et à tous les éléments. Elle se contente de déclencher un appel de règle pour tous les fils du nœud courant. <xsl:template match="* | /"> <xsl:apply-templates/> </xsl:template> Rappelons qu’un appel à xsl:apply-templates sans attribut select est équivalent à la sélection 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. <xsl:template match="text() | @*"> <xsl:value-of select="."/> </xsl:template> 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 : <xsl:template match="processing-instruction() | comment()"/> Voici le programme XSLT minimal : il ne contient aucune règle, à part les règles par défaut qui sont implicites. Exemple 35 Defaut.xsl : Un programme XSLT minimal
<?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> </xsl:stylesheet>

L’application de ce programme à notre document CoursXML.xml donne le résultat suivant : Exemple 36 Defaut.xml : Le résultat des règles par défaut

124
<?xml version="1.0" encoding="UTF-8"?> Publication XSLT

CHAPTER 3. 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.

3.2.3

Déclenchement de règles avec xsl:apply-templates

L’élément xsl:apply-templates désigne un ensemble de nœuds avec une expression XPath, et demande l’application d’une règle pour chaque nœud. L’expression est toujours évaluée en prenant comme nœud contexte le nœud pour lequel la règle contenant xsl:apply-templates a été instanciée. Cet élément a deux attributs, tous deux optionnels : 1. select contient l’expression XPath désignant les nœuds à traiter ; 2. mode est la catégorie des règles à considérer. 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). Instanciation des règles Pour chaque nœud de l’ensemble désigné par l’expression XPath, le processeur va rechercher la règle à appliquer. Il n’y a pas de raison à priori pour que la même règle soit appliquée à tous les nœuds. Prenons l’exemple du programme suivant, appliqué à notre exemple CoursXML.xml. Exemple 37 ApplyTemplates.xsl : Exemple de xsl:apply-template
<?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="//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
</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>

125

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 ENSEIGNANTS . On obtient le résultat suivant : Exemple 38 ApplyTemplates.xml : Résultat du programme précédent
<?xml version="1.0" encoding="ISO-8859-1"?> <!-Application de la règle ENSEIGNANTS --> 1 : Noeud de texte 2 : Noeud de commentaire 3 : Noeud de texte 4 : Noeud NOM 5 : Noeud de texte 6 : Noeud NOM 7 : Noeud de texte

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 ENSEIGNANTS 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. Contexte d’instanciation d’une règle Un élément xsl:apply-templates est associé à une expression XPath qui sélectionne un ensemble de nœuds. Comme nous l’avons vu dans le chapitre 2, pour chaque nœud de cet ensemble, on connaît la position avec la fonction position() et le nombre de nœuds avec la fonction last(). Ces informations servent de contexte à l’instanciation des différentes règles. Pour chaque règle, il existe un nœud courant, celui qui a déterminé la sélection de la règle. On peut de plus faire référence à la position ou à la taille du contexte : en l’occurrence, cela nous a permis de numéroter les nœuds avec position().

…

„

…

„

126

CHAPTER 3. XSLT

3.2.4

Sélection des règles

Regardons maintenant comment une règle est sélectionnée quand plusieurs possibilités existent. Pour chaque nœud sélectionné par un xsl:apply-templates, le processeur va regarder toutes les règles du programme, et tester le pattern comme indiqué précédemment (voir page 119) pour déterminer si le nœud satisfait la règle. Quand aucune règle n’est trouvée, c’est la règle par défaut qui est instanciée. Si une seule règle est trouvée, elle s’applique, ce qui signifie bien qu’elle est prioritaire par rapport à la règle par défaut. Cette même notion de priorité se généralise à la situation où plusieurs règles sont candidates. Le processeur doit alors choisir la règle à instancier en fonction d’un système de priorités. Un autre facteur intervenant dans le choix d’une règle est le mode. Enfin nous avons vu (page 115) que l’importation de programme définit une préséance (à ne pas confondre avec la priorité). Nous décrivons ci-dessous le rôle des attributs priority et mode de l’élément xsl:template, avant de récapituler les principes de choix d’une règle. Le document XML utilisé pour nos exemples dans cette section est une liste de films (trois seulement). Exemple 39 ListeFilms.xml : Une liste de films
<?xml version="1.0" encoding="ISO-8859-1"?> <FILMS> <FILM> <TITRE>Vertigo</TITRE> <ANNEE>1958</ANNEE><GENRE>Drame</GENRE> <MES>Alfred Hitchcock</MES> <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> <FILM> <TITRE>Alien</TITRE> <ANNEE>1979</ANNEE><GENRE>Science-fiction</GENRE> <MES>Ridley Scott</MES> <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> <FILM> <TITRE>Titanic</TITRE> <ANNEE>1997</ANNEE><GENRE>Drame</GENRE> <MES>James Cameron</MES> <RESUME>Conduite par Brock Lovett, une expédition américaine fouillant l’épave du Titanic remonte à la surface le croquis d’une femme nue. Alertée par les médias la dame en question, Rose DeWitt Bukater, aujourd’hui centenaire, rejoint les lieux du naufrage, d’où elle entreprend de conter le récit de son fascinant, étrange et tragique voyage... </RESUME> </FILM> </FILMS>

3.2. LES RÈGLES XSLT
Priorités

127

La priorité d’une règle peut être soit indiquée explicitement avec l’attribut priority, soit calculée implicitement 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 RESUME . Deux règles suffisent : une règle pour les éléments de type RESUME, qui ne fait rien ;

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é. Exemple 40 Priority.xsl : Utilisation de l’attribut priority
<?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/> </xsl:template> <xsl:template match="RESUME"/> <xsl:template match="@*|node()" priority="-1"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>

La troisième règle avec l’attribut match="@*|node()" et avec la priorité -1 s’applique à tous les types de nœuds. Elle commence par copier le nœud courant, puis sélectionne avec xsl:applytemplates tous ses fils, y compris les attributs. Pour chacun de ses fils la même règle s’applique récursivement, jusqu’à ce que la totalité du document soit parcourue et recopiée. Les éléments RESUME constituent la seule exception : la règle appelée en priorité alors est celle qui est spécifique à ce type d’élément, et elle ne produit aucun résultat. On obtient donc le document suivant : Exemple 41 Priority.xml : Résultat du programme Priority.xsl
<?xml version="1.0" encoding="ISO-8859-1"?> <FILMS> <FILM> <TITRE>Vertigo</TITRE> <ANNEE>1958</ANNEE><GENRE>Drame</GENRE> <MES>Alfred Hitchcock</MES>

…

…

„

„

ƒ ƒ

128

CHAPTER 3. XSLT

</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. 2. Le pattern processing-instruction(’nom’) a lui aussi une priorité égale à 0. 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 :

ƒ

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:applytemplates 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 A 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 : <a href=’#Alien’>Lien vers le film Alien</A> 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:apply-templates select="FILM" mode ="Ancres"/> <xsl:apply-templates select="FILM"/> Voici le programme complet : Exemple 42 Mode.xsl : Utilisation de l’attribut mode
<?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" encoding="ISO-8859-1"/>

<xsl:template match="FILMS"> <html> <head><title>Liste des films</title></head> <body bgcolor="white"> <center><table>

… „

ƒ

la dernière règle se divise en deux : – @* a une priorité de -0,5 ; – node() a une priorité de -0,5 ;

130
<xsl:apply-templates select="FILM" mode ="Ancres"/> </table></center> <xsl:apply-templates select="FILM"/> </body> </html> </xsl:template> <xsl:template match="FILM" mode="Ancres"> <td><a href="#{TITRE}"> <xsl:value-of select="TITRE"/> </a></td> </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>

CHAPTER 3. XSLT

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. Algorithme de sélection d’une règle Voici, en résumé, comment le processeur XSLT choisit une règle à instancier pour un nœud sélectionné par xsl:apply-templates. Au départ, la liste des règles candidates est constituée de toutes celles qui ont un attribut match. 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. 2. On teste le pattern de l’attribut match pour déterminer si le nœud satisfait la règle. 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 problè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

Figure 3.3: Affichage du résultat avec Netscape

3.2.5

Appel de règle avec xsl:call-template

Quand une règle utilise l’attribut name au lieu de l’attribut match, on peut l’instancier explicitement avec un xsl:call-template. L’utilisation de xsl:call-template s’apparente aux fonctions dans un langage de programmation classique. Comme les fonctions, la règle appelée doit permettre de factoriser des instructions utilisées en plusieurs endroits du programme. Reprenons l’exemple affichant les nœuds du document CoursXML.xml en les numérotant. Le programme ApplyTemplates.xml (voir page 125) contenait une règle pour chaque type de nœud rencontré, et toutes ces règles produisaient à peu près le même résultat : la position du nœud courant, et un texte. Le programme ci-dessous remplace le corps de ces règles par un appel xsl:call-template à une règle nommée Afficher qui se charge de produire le résultat. Exemple 43 CallTemplate.xsl : Exemple de xsl:call-template
<?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="//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. Exemple 44 CallTemplate.xml : Résultat du programme précédent
<?xml version="1.0" encoding="ISO-8859-1"?> <!-Application de la règle ENSEIGNANTS --> 1 : 2 : Enseignant responsable 3 : 4 : Amann 5 : 6 : Rigaux 7 :

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,

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

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

ƒ ƒ

une instanciation de règle ne « renvoie » pas de valeur ; une instanciation de règle ne prend pas d’argument autre que le nœud auquel elle s’applique ;

Paramètres

3.2. LES RÈGLES XSLT

133

xsl:param name=nom select=expression L’attribut nom est le nom du paramètre, et expression (optionnel) est une expression XPath qui, convertie en chaîne de caractères, donne la valeur par défaut du paramètre. Voici la nouvelle version de la règle Afficher, avec un paramètre nommé texte ayant pour valeur par défaut string(inconnu). <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 inconnu 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 positionnement de xsl:with-param suit des règles précises : 1. il peut apparaître dans le contenu de l’élément xsl:call-template, comme ci-dessus ; c’est d’ailleurs le seul contenu possible pour xsl:call-template ; 2. on peut aussi passer des paramètres à xsl:apply-templates, en les plaçant immédiatement après la balise ouvrante de cet élément. 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

3.3

Instructions de contrôle

Jusqu’à présent nous avons surtout considéré XSLT comme un langage permettant de « déclarer » des actions à exécuter en fonction de certains événements (la rencontre d’un nœud dans le document source). Ce mode de programmation est bien adapté au traitement d’une structure arborescente, et de d’autant plus que ce traitement est orienté vers la production d’un autre document. Certaines manipulations courantes en programmation (tests, boucles, utilisation de variables) seraient cependant impossibles, ou très laborieuses si on devait se restreindre à l’utilisation des règles. XSLT propose les structures classiques de test, d’itération et, dans une certaine mesure, d’utilisation de variables, avec des particularités importantes. La plus notable concerne les variables : d’une part il n’est pas possible de les modifier (il aurait peut-être été plus judicieux de parler de « constante »), et d’autre part une variable est soit locale à une règle, et inconnue alors des autres règles, soit globale au programme et connue alors de toutes les règles. En pratique cela signifie que beaucoup de pratiques habituelles en programmation sont impossibles. Par exemple : 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.

3.3.1

Tests : xsl:if et xsl:choose

L’instruction de test la plus simple est xsl:if. L’élément a un attribut, test, dont la valeur doit être une expression XPath. Cette expression est évaluée, puis convertie en booléen. Le contenu de l’élément xsl:if est instancié si l’évaluation donne true. L’exemple suivant montre l’utilisation de xsl:if pour ne garder dans le document ListeFilms.xml (voir page 126) que les films qui sont parus avant 1970. Exemple 45 If.xsl : Utilisation de xsl:if
<?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="FILMS"> <xsl:apply-templates select="FILM"/> </xsl:template> <xsl:template match="FILM"> <xsl:if test="ANNEE &lt; 1970"> <xsl:copy-of select="."/>

3.3. INSTRUCTIONS DE CONTRÔLE
</xsl:if> </xsl:template> </xsl:stylesheet>

135

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 « » ne peut être utilisé directement dans le test et doit être remplacé par une référence à l’entité prédéfinie &lt;. 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 : xsl:when est semblable à xsl:if, et permet d’exprimer un des choix ;

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. Exemple 46 Choose.xsl : Utilisation de xsl:choose
<?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="FILMS"> <xsl:apply-templates select="FILM"/> </xsl:template> <xsl:template match="FILM"> <xsl:choose> <xsl:when test="ANNEE &lt; 1960"> "<xsl:value-of select="TITRE"/>" est ancien </xsl:when> <xsl:when test="ANNEE &gt;= 1960">

„

ƒ ƒ

136
"<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>

CHAPTER 3. XSLT

3.3.2

Boucles : xsl:for-each

L’instruction xsl:for-each est la seule structure explicite d’itération dans XSLT (l’autre possibilité étant d’appeler récursivement une règle nommée, comme nous le verrions plus loin). Le while n’existe pas puisqu’il suppose la possibilité de modifier, dans le corps de la boucle, la condition qui détermine l’arrêt. Or, comme nous l’avons signalé, il n’existe pas dans XSLT de « variable » au sens courant du terme (nom auquel on peut affecter une valeur). Dans ces conditions le while n’apporterait rien de plus que xsl:for-each. L’itération s’effectue toujours sur un ensemble de nœuds constitué par l’expression XPath de l’attribut select. Elle consiste à prendre successivement comme nœud courant chaque nœud de l’ensemble, et à instancier le contenu de l’élément xsl:for-each en prenant pour contexte : 1. ce nœud courant ; 2. la taille de l’ensemble (fonction last()) ; 3. la position du nœud courant dans l’ensemble (fonction position()). Il s’agit des trois informations habituelles définissant un contexte XPath. Il faut bien noter que ce contexte est local à xsl:for-each au sein d’une règle, et que le nœud courant redevient celui pour lequel la règle a été instanciée dès que la boucle est terminée. Prenons l’exemple suivant, basé sur le document CoursXML.xml (page 110). On veut parcourir toutes les séances de cours, afficher pour chacune son numéro, le nombre total de séances et l’intitulé de la séance. Le programme est basé sur xsl:for-each4 : Exemple 47 ForEach.xsl : Utilisation de xsl:for-each
<?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="/"> <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
select="concat(position(), ’/’, last())"/> : <xsl:value-of select="."/> </xsl:for-each> </SEANCES> Valable pour l’année <xsl:value-of select="ANNEE"/> </xsl:template> </xsl:stylesheet>

137

On peut noter tout de suite qu’il aurait été possible d’obtenir exactement la même chose avec xsl:applytemplates 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éclarativité (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. Exemple 48 ForEach.xml : Résultat de ForEach.xsl
<?xml version="1.0" encoding="ISO-8859-1"?> <PROGRAMME> Programme 1/1 : <SEANCES> Séance 1/2 : Documents XML Séance 2/2 : Programmation XSLT </SEANCES> Valable pour l’année 2002 </PROGRAMME>

L’élément xsl:for-each est inclus dans le corps d’une règle qui a été instanciée pour le nœud PROGRAMME 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 : <xsl:value-of select="concat(position(), ’/’, last())"/> : <xsl:value-of select="."/> 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 SEANCE fils de PROGRAMME (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 PROGRAMME . Le fragment :

Valable pour l’année <xsl:value-of select="ANNEE"/>

3.3.3

Variables : xsl:variable

Une variable XSLT est un nom (donné par l’attribut name) associée à une valeur. Cette valeur peut être définie de deux manières :

par une expression XPath donnée dans l’attribut select ;

par le contenu de l’élément.

Dans le premier cas le type de la variable est l’un des quatre types XPath (booléens, numériques, chaînes et node-set). Dans le second cas la variable appartient à un type spécifique à XSLT, dit result tree fragment : dans la version 1.0 de XSLT, une valeur de ce type correspond à une chaîne de caractères qui représente la version sérialisé du fragment calculé. Ceci signifie surtout qu’il n’est pas possible d’extraire des valeurs d’un fragment résultat en utilisant des axes XPath. Voici quelques exemples de définition de variables :

xsl:variable name=’var1’ select=’12’/ : définit une variable var1 avec une valeur 12 ; xsl:variable name=’var2’ select=’/COURS/ENSEIGNANTS’/ : définit une variable var2 qui prend pour valeur les nœuds de type ENSEIGNANTS, petits-fils de la racine du document ;

Enfin la variable suivante est définie par son contenu : <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 exemple, 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 nodeset. 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. Portée d’une variable La portée d’une variable est la partie du programme dans lequel la variable est définie. On distingue deux types de variables :

Les variables globales sont définies par des éléments xsl:variable de premier niveau, fils de l’élément xsl:stylesheet . Ces variables sont visibles dans tout le programme, même dans des règles qui précèdent leur définition (rappelons que l’ordre des éléments de premier niveau n’est pas significatif, sauf pour xsl:import). Les variables locales apparaissent dans un corps de règle. Une variable locale est visible dans tous les frères droits (following-sibling) du nœud xsl:variable, ainsi que dans leurs descendants.

…

…

„

insère donc logiquement l’année 2002, contenu textuel du seul fils de

…

…

„

…

„ ƒ

„ ƒ

ƒ

ƒ

ƒ

ƒ

ƒ

„

PROGRAMME

de type ANNEE.

3.3. INSTRUCTIONS DE CONTRÔLE

139

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

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:calltemplate. 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 programme 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:output method="xml" encoding="ISO-8859-1"/>

<xsl:variable name="annee" select="1970"/> <xsl:template match="FILMS"> <xsl:apply-templates select="FILM"/> </xsl:template> <xsl:template match="FILM"> <xsl:choose> <xsl:when test="ANNEE &lt; $annee"> "<xsl:value-of select="TITRE"/>" est ancien </xsl:when> <xsl:when test="ANNEE &gt;= $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

ƒ ƒ

une des deux variables est une variable globale ; les deux variables sont globales mais n’ont pas la même préséance.

140
<?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="/"> <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> Valable pour l’année <xsl:value-of select="ANNEE"/> </xsl:template> </xsl:stylesheet>

CHAPTER 3. XSLT

La variable $phrase stocke, après évaluation de xsl:value-of, la chaîne de caractères : (valable pour l’an 2002) qui est ensuite réutilisée dans : <xsl:value-of select="concat(., $phrase)"/> La portée de la variable est limitée au contenu de l’élément SEANCES . 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).

3.3.4

Tri : xsl:sort

XSLT fournit un élément pour trier les nœuds sélectionnés par un xsl:apply-templates ou un xsl:for-each. Par défaut, les nœuds sont triés sur leur position, et suit l’ordre du document pour les forward-axes, ou l’ordre inverse pour les reverse-axes (voir page 103). Le principal attribut de xsl:sort est select qui indique le critère (la clé) de tri. On peut indiquer plusieurs xsl:sort successifs : le processeur effectue un premier tri avec la premiè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. Voici un exemple de tri sur le document ListeFilms.xsl (page 126). On trie d’abord sur l’année de parution du film, puis, pour les films parus la même année, sur le titre.

…

„

3.3. INSTRUCTIONS DE CONTRÔLE
Exemple 51 Sort.xsl : Exemple de tri
<?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"/>

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>

On obtient le résultat suivant : Exemple 52 Sort.xml : Résultat du tri
<?xml version="1.0" encoding="ISO-8859-1"?> <FILMSTRIES> <b>Année : </b>1958, Vertigo réalisé par Alfred Hitchcock <b>Année : </b>1979, Alien réalisé par Ridley Scott <b>Année : </b>1997, Titanic réalisé par James Cameron </FILMSTRIES>

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 ANNEE et TITRE 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 datatype 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.

3.3.5

Itérations par récursion

Nous concluons cette présentation des instructions de contrôles de XSLT en présentant quelques techniques avancées de programmation, et notamment l’exécution de boucles. Nous avons vu que la seule instruction pour effectuer des boucles est xsl:for-each, et qu’elle est essentiellement destinée à parcourir un ensemble de nœuds pour insérer du texte dans le document résultat. En programmation « classique », on utilise couramment les boucles pour effectuer une tâche un nombre fixé de fois, ou pour calculer une valeur. Il n’y a pas à priori de construction pour ce type de fonctionnalité en XSLT :

…

„

…

„

142

CHAPTER 3. XSLT

1. boucle de 1 à : xsl:for-each s’effectue autant de fois qu’il y a de nœuds dans l’ensemble à traiter : on ne peut pas jouer sur ce nombre de nœuds puisque le document source n’est pas modifiable, et on ne peut pas se baser sur un compteur puisque les variables non plus ne sont pas modifiables ; 2. calcul de valeur : typiquement on utilise une variable dans laquelle on stocke au fur et à mesure le résultat du calcul ; pour les mêmes raisons que précédemment ça n’est pas possible avec XSLT. 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 : 1. si le nombre maximal à afficher est dépassé, elle ne fait rien ; 2. sinon elle affiche l’élément correspondant au numéro courant, et s’appelle récursivement en incrémentant ce numéro. Voici la règle. Elle fonctionne pour tout ensemble de nœuds. <xsl:template name="AfficheElement"> <xsl:param name="numero"/> <xsl:param name="liste"/> <xsl:param name="max"/> <xsl:if test="$numero &lt;= $max"> Elément <xsl:value-of select="$numero"/> : <xsl:value-of select="$liste[position()=$numero]"/> <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:variable name="maxFilms" select="2"/> <xsl:variable name="listeFilms" select="/FILMS/FILM/TITRE"/> <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 $maxFilms 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 totalisation, 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. Exemple 53 Notes.xml : Des notes sur les films
<?xml version="1.0" encoding="ISO-8859-1"?> <FILMS> <FILM> <TITRE>Vertigo</TITRE> <NOTES> <NOTE>3</NOTE><NOTE>3</NOTE><NOTE>3</NOTE><NOTE>5</NOTE> </NOTES> </FILM> <FILM> <TITRE>Alien</TITRE> <NOTES> <NOTE>4</NOTE><NOTE>3</NOTE><NOTE>5</NOTE> </NOTES> </FILM> <FILM> <TITRE>Titanic</TITRE> <NOTES> <NOTE>2</NOTE><NOTE>2</NOTE><NOTE>4</NOTE><NOTE>1</NOTE> </NOTES> </FILM> <FILM> <TITRE>Sleepy Hollow</TITRE> <NOTES> <NOTE>2</NOTE><NOTE>4</NOTE><NOTE>4</NOTE> </NOTES> </FILM> <FILM> <TITRE>American Beauty</TITRE> <NOTES> <NOTE>5</NOTE><NOTE>4</NOTE><NOTE>4</NOTE> </NOTES> </FILM> <FILM> <TITRE>Impitoyable</TITRE> <NOTES> <NOTE>2</NOTE><NOTE>1</NOTE><NOTE>5</NOTE> </NOTES> </FILM> </FILMS>

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). fonction Somme (Liste l) début si (l n’est pas vide) retourner l[1] + Somme (l[2..n]) sinon retourner 0 fin 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 beaucoup 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> <xsl:value-of select="$note + $autresNotes"/> </xsl:when> <xsl:otherwise>0</xsl:otherwise> </xsl:choose> </xsl:template> 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. Exemple 54 BoucleNotes.xsl : Une boucle sur une liste
<?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" encoding="ISO-8859-1"/> <xsl:template match="FILMS">

3.3. INSTRUCTIONS DE CONTRÔLE
<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:value-of select="$note + $autresNotes"/> </xsl:when> <xsl:otherwise>0</xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>

145

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

Figure 3.4: L’affichage du résultat avec Netscape orienté vers la transformation de documents XML, et capable occasionnellement d’effectuer des tâches de programmation classiques.

3.4

Évaluation d’un programme XSLT

Nous concluons ce chapitre en décrivant pas à pas l’évaluation d’un programme XSLT. L’objectif est de récapituler une nouvelle fois les principaux concepts d’une transformation XSLT : nœud courant et nœud contexte, instanciation de règle, production du résultat, etc.
Document Element COURS
Attr CODE TC234

Element ENSEIGNANTS Element NOM Texte Rigaux Element NOM Texte Waller
Attr ID 2

Element SUJET Texte Publication XSLT Element SEANCE Texte Documents XML

Element PROGRAMME Element SEANCES Element SEANCE
Attr ID 3

Element ANNEE Texte 2002

Texte Programmation XSLT

Figure 3.5: Exemple de référence pour XSLT

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
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:output method="html" encoding="ISO-8859-1"/> <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>

147

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
Document -

CHAPTER 3. XSLT

Figure 3.6: Début de l’évaluation

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

xsl:value-of

Attr select COURS/SUJET

Figure 3.7: Après instanciation de la première règle

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 produites 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:applytemplates 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 ».

Passons maintenant à l’instruction xsl:apply-templates. L’attribut select manque, donc le processeur applique le choix par défaut qui consiste à sélectionner les fils du nœud courant, soit en l’occurrence COURS , l’élément racine du document. C’est cet élément qui devient le nœud courant.
5 Il s’agit de l’algorithme de référence, mais bien entendu chaque processeur est libre d’implanter le sien pourvu que le résultat soit le même.

…

„

3.4. ÉVALUATION D’UN PROGRAMME XSLT
Document -

149

html

head

body

title

xsl:apply-templates

XSLT

Figure 3.8: Après évaluation de xsl:value-of 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, SUJET , ENSEIGNANTS et PROGRAMME . 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

xsl:value-of

xsl:apply-templates

xsl:for-each

Attr select .

Attr select NOM

Attr select SEANCE

xsl:call-template

Figure 3.9: Après évaluation de xsl:apply-templates

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

…

„

…

„ …

„

Attr name AfficheSeance

150

CHAPTER 3. XSLT

exemple, le nœud courant dans l’arbre source est SUJET , et l’ensemble de nœuds est constitué des trois fils de COURS . À 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) :

2. évaluer l’expression NOM de l’attribut xsl:select du xsl:apply-templates, le nœud courant étant ENSEIGNANTS : 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 PROGRAMME ) 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:foreach. Il reste au processeur à évaluer deux appels xsl:call-template à la règle nommée AfficheSeance. 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

XSLT

li

li

xsl:call-template

xsl:call-template

Amann

Rigaux

Attr name AfficheSeance

Attr name AfficheSeance

Figure 3.10: Après évaluation du xsl:for-each

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.

Il reste à sérialiser le document obtenu, ce qui donne le résultat ci-dessous6 .
hr/

d

™

6 Dans le cas d’un document HTML, certaines transformations peuvent intervenir, comme la transformation d’un hr . Elles sont décrites dans le chapitre suivant.

…

„

1. évaluer xsl:value-of, avec l’attribut select valant « . », le nœud courant étant résultat est XSLT ;

…

…

„

„

…

…

„

SUJET : le

„

en

d

™

3.4. ÉVALUATION D’UN PROGRAMME XSLT
Document -

151

html

head

body

title

h1

h1

h1

XSLT

center

ol

ul

XSLT

li

li

li

li Séance 2/2 : Programmation XSLT

Amann

Rigaux

Séance 1/2 : Documents XML

Figure 3.11: Le résultat final Exemple 56 Recap.html : le document HTML sérialisé
<html> <head> <META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Publication XSLT</title> </head> <body bgcolor="white"> <h1> <center>Publication XSLT</center> </h1> <h1>Enseignants</h1> <ol> <li>Amann</li> <li>Rigaux</li> </ol> <h1>Programme</h1> <ul> <li> S&eacute;ance 1/2 : Documents XML</li> <li> S&eacute;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 : 1. le processeur construit un arbre, et pas une version sérialisée du document ; 2. chaque instruction est placée dans le document résultat, puis évaluée ; elle reste toujours associée à un nœud courant dans le document source, lui-même faisant partie d’un ensemble de de

152

CHAPTER 3. XSLT
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

Production de documents XML
Sommaire
4.1 Définition de Types de Documents : DTD . . . . . . . . . . . 4.1.1 Pourquoi définir une DTD ? . . . . . . . . . . . . . . . 4.1.2 Entités . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.3 Éléments . . . . . . . . . . . . . . . . . . . . . . . . 4.1.4 Attributs . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.5 DTD et XML Schéma . . . . . . . . . . . . . . . . . . Site Web: HTML . . . . . . . . . . . . . . . . . . . . . . . . 4.2.1 HTML et XHTML . . . . . . . . . . . . . . . . . . . 4.2.2 DTD XHTML . . . . . . . . . . . . . . . . . . . . . . 4.2.3 Génération de pages HTML: xsl:output . . . . . . 4.2.4 Transformation d’une page XML . . . . . . . . . . . . 4.2.5 Création de liens . . . . . . . . . . . . . . . . . . . . 4.2.6 XML/XSLT: une solution du problème des liens cassés 4.2.7 Intégration de pages XML . . . . . . . . . . . . . . . Présentations multimédia: SMIL . . . . . . . . . . . . . . . 4.3.1 SMIL par un exemple . . . . . . . . . . . . . . . . . . 4.3.2 DTD SMIL . . . . . . . . . . . . . . . . . . . . . . . 4.3.3 Génération de présentations SMIL . . . . . . . . . . . Traitement de texte dans XSLT . . . . . . . . . . . . . . . . 4.4.1 xsl:preserve-space et xsl:strip-space . . 4.4.2 Génération de texte : xsl:text . . . . . . . . . . . . 4.4.3 Sérialisation du résultat : xsl:output . . . . . . . . RSS et RDF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 154 154 156 160 164 165 165 166 170 171 173 175 176 177 177 181 187 195 195 197 197 197

4.2

4.3

4.4

4.5

Nous allons maintenant étudier l’utilisation de XSLT pour la traduction de documents XML dans diffé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 contente 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.

4.1
4.1.1

Définition de Types de Documents : DTD
Pourquoi définir une DTD ?

La définition de DTD pour la description de documents XML offre plusieurs avantages, liés à la possibilité d’un contrôle plus élevé dans la production du documents et des programmes qui exploitent ces documents :

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

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 :

ƒ ƒ ƒ ƒ ƒ ƒ ƒ

Production de documents : un éditeur XML peut se servir de la DTD pour faciliter l’édition d’un document. Par exemple, connaissant la structure du document à produire, il peut proposer à chaque moment les éléments et attributs qui peuvent être insérés à un endroit précis du document (édition structurée). Il est également possible avant la sauvegarde du fichier XML de vérifier sa conformité par rapport à sa DTD. Programmation XSLT : un auteur de programme XSLT se base sur la DTD du/des document(s) à transformer et du/des documents à produire pour l’écriture et la vérification des règles XSLT. Échange de documents XML : dans un contexte où un « consommateur » d’information se déclare prêt à recevoir ces informations au format XML, la DTD sert à décrire précisément les règles de structuration des documents reçus ; Fragmentation de document XML : les entités qui constituent un document XML sont déclarées dans sa DTD. Les définitions d’entités correspondent à une description physique de la structure du document, qui est est indépendante de la notion de « dialecte » XML.

Entités

plusieurs fichiers XML, dites entités XML (parsed entities) ; un fichier DTD optionnel ; 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 document 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 indirectement. 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 "&#219;">

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 identification plus formelle et surtout indépendante d’une adresse physique. Il est surtout appliqué à des documents « 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 obligatoirement 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 title de XHTML ne peut contenir que du texte : <!ELEMENT title (#PCDATA)> 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 html 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 C/ doit apparaître au moins une fois, et dans le deuxième cas, tous les éléments B/ doivent apparaître avant tous les éléments C/ . 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 A zéro ou plusieurs éléments de type B et C 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 <!ELEMENT A (B|C)*>

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

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+))>

Selon cette définition, un tableau HTML minimal a la forme suivante : <table> <tr>...</tr> </table> ou <table> <tbody>...</tbody> </table> Les éléments tr définissent des lignes du tableau. Pour créer un tableau complet avec des colonnes, il faut également connaître la définition de l’élément de type tr : <!ELEMENT tr

On voit que ce tr a comme fils un ou plusieurs éléments de type th et/ou td dans n’importe quel ordre. Chaque élément d’un de ces deux types définit une colonne dans le tableau. La différence entre th et td est que, dans le premier cas (th), le contenu est en général affiché en gras (header). 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 td contiennent du texte) : Exemple 57 TableauSimple.html : Tableau 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>Page XHTML avec un tableau simple</title> </head> <body>

…

„

…

…

„

„

ƒ ƒ ƒ

les éléments caption, thead et tfoot sont optionnels ; 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) ; 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)

(th|td)+>

4.1. DÉFINITION DE TYPES DE DOCUMENTS : DTD
<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>

159

Le tableau a deux lignes, indiquées par les balises tr dont chacune a deux colonnes (balises td ). La figure 4.1 montre l’affichage par de cette page par Netscape.

Figure 4.1: Affichage de TableauSimple.html. 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 : <!ELEMENT td "(#PCDATA | table | p | b | em )*">

Cette dernière définition indique que les éléments de type td contiennent des sous-éléments de diffé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 donné peut contenir des types d’éléments qui utilisent déjà 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) ,: Exemple 58 TableauComplexe.html : Exemple d’un tableau imbriqué
<?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>Page XHTML avec un tableau imbriqué</title>

e

f

…

„

e

…

„

160

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

</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>

--> -->

--> -->

Figure 4.2: Copie d’écran de l’affichage d’un tableau imbriqué

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 : <!ATTLIST table border CDATA #IMPLIED> 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 : <!ATTLIST td bgcolor CDATA #IMPLIED

4.1. DÉFINITION DE TYPES DE DOCUMENTS : DTD
rowspan CDATA "1" colspan CDATA "1">

161

L’attribut optionnel bgcolor permet de définir la couleur de fond de la cellule. Les deux attributs (optionnels) suivants spécifient respectivement la hauteur (en lignes) et la largeur (en colonnes) de la cellule. Par défaut ces attributs prennent la valeur 1 : dans le cas d’un attribut optionnel avec valeur par défaut, le mot réservé #IMPLIED est remplacé par la valeur par défaut. Voici l’exemple d’un tableau plus complexe où les différents éléments ont des attributs (voir l’affichage dans la figure 4.3). Exemple 59 TableauAttrs.html : Tableau avec attributs
<?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>page xhtml avec un tableau complexe</title> </head> <body> <table border="1"> <tr><th rowspan="2"/><th bgcolor="gray" colspan="2">R</th></tr> <tr bgcolor="#aaaaaa"><th>A</th><th>B</th></tr> <tr><th bgcolor="#aaaaaa">x</th><td>1</td><td>2</td></tr> </table> </body> </html>

Figure 4.3: Affichage de TableauAttrs.html La présence de certains attributs est parfois obligatoire dans la balise ouvrante d’un élément. Par exemple, la balise img 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 alt height width

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.

…

„

CDATA CDATA CDATA CDATA

#REQUIRED #REQUIRED #IMPLIED #IMPLIED>

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 : <!ATTLIST img ismap (ismap) #IMPLIED>

Balise <img src="lo.gif" alt="logo"> <img src="lo.gif" alt="logo" width="100"> <img src="lo.gif" alt="logo" ismap="ismap"> <img> <img src="lo.gif" alt="logo" ismap="toto"

Table 4.1: Balises ouvrantes de type img

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) : <!ATTLIST button type (button|submit|reset) "submit"> 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éclaration 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 : <!ATTLIST html xmlns CDATA #FIXED ’http://www.w3.org/1999/xhtml’>

Cet attribut définit l’espace de nommage des éléments XHTML Et ne peut pas être changé. Des exemples de balise html ouvrantes valides et non-valides sont donnés dans le tableau 4.2. Balise <html> <html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://foo.bar/xhtml">

Table 4.2: Balises ouvrantes d’éléments de type html

Types d’attributs À part CDATA il existe neuf autres types d’attributs qui sont résumés dans le tableau 4.3.

…

„

Des exemples de balises

img

ouvrantes valides et non-valides sont donnés dans le tableau 4.1. Valide ? valide valide valide invalide : les attributs src et alt manquent invalide : "toto" n’est pas permis comme valeur de ismap

…

„

Valide ? valide valide invalide : espace de noms différent de la valeur fixée

4.1. DÉFINITION DE TYPES DE DOCUMENTS : DTD
Type d’Attribut CDATA (nom1|nom2|...) NMTOKEN NMTOKENS ID IDREF IDREFS Valeurs possibles chaînes de caractères composée de tous les caractères sauf &, < et > un nom symbolique (chaîne de caractères sans blancs) dans l’ensemble {nom1, nom2, ...} nom symbolique liste de noms symboliques nom symbolique: identificateur de l’élément dont la balise contient l’attribut de ce type (voir page 163) nom symbolique: référence vers un élément identifié par un attribut de type ID (voir section 4.1.4 liste de noms symboliques (séparés par des blancs) : référence vers des éléments identifiés par des attributs de type ID (voir page 163) nom symbolique : adresse d’une entité externe non-XML (voir page 154) liste de noms symboliques : adresses d’entités externes non-XML (voir page 154)

163

ENTITY ENTITIES

Table 4.3: Types d’attributs et valeurs possibles

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 : <!ATTLIST table id 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 : 1. On peut définir au maximum un attribut identificateur par type d’élément. 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 : <!ATTLIST label for IDREF #IMPLIED>

164

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

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 : <form action="..." method="post"> <table> <tr> <td><label for="fname">first name</label> <td><input type="text" name="firstname" id="fname"> <tr> <td><label for="lname">last name</label> <td><input type="text" name="lastname" id="lname"> </table> </form> Ce formulaire (élément de type form) contient deux éléments de type input qui sont identifiés par un attribut id de type ID. Les valeurs de ces deux attributs apparaissent comme références dans les attributs for des éléments de type label. L’utilisation d’attributs de type IDREF et IDREFS dans un document XML doit respecter la contrainte suivante : pour chaque valeur (identificateur) d’un attribut de type IDREF et chaque valeur dans la liste des valeurs d’un attribut de type IDREFS il doit exister un élément avec cet identificateur, c’est-à-dire avec un attribut de type ID de valeur identique. Dans le domaine des bases de données relationnelle, on parle de « contraintes d’integrité référentielle ».

4.1.5

DTD et XML Schéma

Concluons cette présentation des DTD par quelques remarques sur leurs limites :

Il n’est pas possible de spécifier des structures complexes sur des contenus mixtes : la structure d’un élément qui peut avoir des fils de type Text ne peut pas être détaillée à un niveau suffisant (il est seulement possible de spécifier les types des fils possibles, sans imposer une autre contrainte).

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

…

„

…

„

ƒ ƒ ƒ ƒ

Une DTD n’est pas un document XML : ceci crée une séparation inutile entre la description de la structure et le contenu d’un document qui ne peuvent pas être traités de manière uniforme. Par exemple, il n’est pas possible d’inspecter une DTD dans un programme XSLT ! L’ensemble des types proposés par une DTD est restreint : on ne peut utiliser qu’un seul type de valeur atomique #PCDATA pour le contenu et une dizaine de types pour les attributs (CDATA, NMTOKEN, ID, IDREF, ....). Des types très courants dans les langages de programmation et la gestion de données en général (entiers, flottants, dates) sont complètement absents. Il n’est pas possible de dire explicitement que deux types d’éléments ont la même structure : souvent différents types de balise partagent une partie de leur structure (modèle de contenu). Un exemple dans XHTML sont les balises td et th qui ont exactement le même modèle de contenu. Une séparation entre les balises et leurs modèles de contenu donnerait plus de flexibilité et de modularité dans la définition du langage (la DTD XHTML « simule » cette séparation avec l’utilisation d’entités paramètres).

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.

4.2

Site Web: HTML

Nous passons maintenant à l’étude de quelques DTD importantes définissant des langages couramment utilisés sur le web.

4.2.1

HTML et XHTML

HTML est un langage de balisage qui, contrairement à XML, fournit un ensemble fixe de balises avec des significations précises qui permettent aux différents navigateurs HTML de présenter le contenu du document d’une manière adaptée. Comme toutes les langages informatiques qui doivent s’adapter à des nouveaux besoins, HTML a évolué depuis sa première version publiée en 1991. En tout, entre 1991 et 1999 il y a eu quatre versions succesives de HTML, numérotées de 1.0 à 4.0. Bien que les documents HTML actuels ressemblent dans leur structure à des documents XML, il ne sont pas conformes à la recommandation XML. Ceci s’explique en partie par le fait que HTML est apparu avant XML et s’est fondé sur le standard SGML. Ce dernier, qui peut être considéré comme un précurseur de XML, permet plus de souplesse dans l’utilisation des balises et des attributs. En contrepartie cette souplesse rend la tâche d’analyse syntaxique d’un document HTML plus complexe. Exemple 60 ExHTML4.html : Page HTML Version 4.0
<html> <head> <title>Exemple de Page HTML 4.0</title> </head> <BODY> <P>Paragraphe 1 <br><img SRC="image.gif" width=30pt ismap> <p>Paragraphe 2 </body> </html>

Le fichier ExHTML4.html n’est pas conforme à XML pour plusieurs raisons :

Quelques types d’éléments non-vides permettent aux auteurs d’omettre les balises fermantes. Par exemple, les balises P et p ne sont pas obligatoirement fermées. Les règles pour la fermeture implicite d’une balise non-fermée sont définies par le standard SGML. Dans HTML, les noms des balises et des attributs ne sont pas sensibles à la casse (case-sensitive). Les balises P et p , qui correspondent à des débuts de paragraphe, sont considérées comme équivalentes. Dans certains cas, il est possible de définir la valeur d’un attribut sans l’entourer par des apostrophes simples ou des guillemets. C’est le cas par exemple pour la valeur de l’attribut width dans la balise img . Enfin les attributs booléens peuvent être utilisés sans valeur. Par exemple, l’attribut ismap dans la balise img n’a pas de valeur.

…

„

…

„ 7…

… „

„

…

… „

… „

„

… „

…

…

„

„

ƒ ƒ ƒ ƒ ƒ

Dans SGML, les balises d’éléments vides ne doivent pas être fermées. Par exemple, la balise br définit un élément vide qui correspond à un saut de ligne dans l’affichage. La représentation XML d’un tel élément ( br/ ou br /br ) n’est pas valide dans HTML 4.0.

166 XHTML

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

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 :

4.2.2

Cette section est consacrée à la structure générale d’un document XHTML. Une description complète de toutes les balises HTML/XHTML dépasserait largement le cadre de ce livre : nous vous renvoyons à l’ouvrage HTML et XHTML, la Référence, de Chuck Musciano & Bill Kennedy, paru aux Éditions o’Reilly. Notre but est essentiellement de donner une classification des différentes balises dans la DTD XHTML par rapport à leur utilisation. Nous ne décrivons pas par exemple les parties de HTML 4.0 qui sont couvertes par la recommandation XML, comme par exemple le problème de l’encodage et l’affichage des caractères. Types de Base En plus des différents types d’attributs définis dans la recommandation XML (CDATA, ID, IDREF, NMTOKEN, . . . ), 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 correspondent à 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 commentaire. 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 correspond à la destination d’un lien HTML :

ƒ ƒ ƒ

toutes les balises d’éléments (vides et non-vides) sont fermées ; les noms des balises et des attributs sont tous écrits en minuscules ; tous les attributs ont une valeur entourée par des guillemets ou des apostrophes simples.

DTD XHTML

4.2. SITE WEB: HTML
<!ATTLIST a href %URI; #IMPLIED

167

Voici la définition de quelques autres types de base dans la DTD XHTML : <!ENTITY % Number "CDATA"> <!-- one or more digits --> <!ENTITY % Text "CDATA"> <!-- used for titles etc. --> <!ENTITY % Length "CDATA"> <!-- nn for pixels or nn% for percentage length --> <!ENTITY % Color "CDATA"> <!-- There are also 16 widely known color names with their sRGB values: Black = #000000 Green = #008000 Silver = #C0C0C0 Lime = #00FF00 Gray = #808080 Olive = #808000 White = #FFFFFF Yellow = #FFFF00 Maroon = #800000 Navy = #000080 Red = #FF0000 Blue = #0000FF Purple = #800080 Teal = #008080 Fuchsia= #FF00FF Aqua = #00FFFF --> Nous verrons plus loin quelques attributs qui utilisent ces différents types. Structure générale d’un document XHTML Le fichier ExXHTML.xml, page 166 donne la structure générale d’un document XHTML. Il commence par une déclaration XML, optionnelle, mais utile pour le processeur. Les trois lignes suivantes définissent la DTD du document : <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> La première ligne indique que l’élément racine du document est de type html. Les deux lignes suivantes définissent l’identificateur public et l’adresse physique de la DTD XHTML qui doit être respectée par le document. Cette DTD est stockée dans le fichier xhtml1-transitional.dtd. L’élément racine html y est défini de la manière suivante : <!ELEMENT html (head, body)> <!ATTLIST html %i18n; xmlns %URI; #FIXED ’http://www.w3.org/1999/xhtml’> Les attributs définis dans l’entité paramètre i18n permettent d’indiquer le code de la langue utilisée dans le contenu du document. L’attribut xmlns défini l’espace de noms de HTML qui ne peut pas être changé (c’est un attribut fixé). Les éléments de type head et body contiennent respectivement l’en-tête et le contenu du document XHTML. Ils sont définis comme suit (nous ne donnons pas les attributs de ces éléments) : <!ELEMENT head (%head.misc;, ((title, %head.misc;, (base, %head.misc;)?) | (base, %head.misc;, (title, %head.misc;))))> <!ELEMENT body %Flow;> Élément entête head : On voit que les éléments de type head doivent avoir exactement un sous-élément de type title, un sous-élément optionnel de type base et des sous-éléments définis par l’entité paramètre head.misc :

…

„

168

CHAPTER 4. PRODUCTION DE DOCUMENTS XML
<!ENTITY % head.misc "(script|style|meta|link|object|isindex)*"> À 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 : <!ELEMENT META EMPTY> <!ATTLIST META %i18n; http-equiv NAME name NAME content CDATA scheme CDATA >

#IMPLIED #IMPLIED #REQUIRED #IMPLIED

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 name 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 : <!ENTITY % Flow "(#PCDATA | %block; | form | %inline; | %misc;)*"> 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 construction de pages avec cadres (frames). Exemple 62 ExXHTMLCompl.html : Page HTML typique.
<?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "../DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Exemple de Page XHTML</title> </head> <body> <center> <h1>Header 1 (centré)</h1> </center> <h2>Header 2</h2> <p>Paragraphe 1</p>

…

„

4.2. SITE WEB: HTML

169

<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. Formattage de texte HTML Le texte de cette page est mis en forme de la façon suivante :

séparation de paragraphes et de lignes: – la balise p est utilisée pour définir quatre paragraphes – les auteurs de pages HTML remarquerons que ces balise sont fermées. – les balises br et hr 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. 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 pre 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 à preserve.

taille de police : le texte entre les h1 et h2 a une taille supérieure au texte normal : HTML définit six niveaux d’agrandissement avec six types de balises ( h1 , h2 h3 , h4 , h5 , h6 ) où la balise h1 correspond à l’agrandissement le plus important.

i

h i

h ji

h i

i

h

h

i

h

i

h

i

i

i

h

h

h

i

i h h

i

h i

h

g g g g

le texte dans la balise

center

est centré ;

170

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

Figure 4.4: Copie d’écran de ExXHTMLCompl.html

Il contient également deux listes, une image et un lien :

HTML permet de construire des listes non-numérotées définies par les éléments ul et les listes numérotés représentées par les balises ol . Les éléments dans une liste sont représentés par les balises li qui sont des éléments « container ».

Les liens entre pages HTML et entre une page HTML et un autre type de ressource sont définis en utilisant la a : les liens sont uni-directionnel – il peuvent être traversés seulement dans une direction – et mono-cible – il n’est pas possible de spécifier plusieurs ressources cibles pour un lien.

Nous montrerons d’autres exemples dans la suite.

4.2.3

Génération de pages HTML: xsl:output

Tous les navigateurs HTML actuels n’ont pas encore adopté le standard XHTML et il faudra continuer encore pendant un certain temps de produire des documents HTML conformes aux versions antérieures

i

h

Les images sont incluses en utilisant la balise

img ;

i

h

i

h

i

h

couleur de police : La balise du texte qu’elle contient.

font permet, entre autre, le changement de la couleur et de la taille

i h

i

h

i h

i

h

g g g g g

style de police : les balises em et b transforment le texte qu’ils contiennent en italique et en gras. Il est évidemment possible de combiner les deux.

4.2. SITE WEB: HTML

171

(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 br et img 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 formattage à 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 IMG src=’logo-small.gif’ .

4.2.4

Transformation d’une page XML

Voyons maintenant comment on peut utiliser XSLT pour publier des informations contenues dans des documents XML sous forme d’un site HTML. Entre autres avantages d’une manipulation des documents XML à la place de documents HTML, nous allons montrer que XSLT permet d’éviter des redondances dans les contenus de documents, et de traiter le problème des liens cassés. Reprenons l’exemple des films et des cinémas de l’introduction pour décrire plus en détail la génération d’un site Web/HTML. Le premier fichier XML est Vertigo.xml, donné page 24. Le programme Film.xsl, page 25 transforme ce document en page HTML. Voici une règle très simple de transformation pour le film : <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
<!ELEMENT FILM (TITRE, AUTEUR, ANNEE, GENRE, PAYS, RESUME) > <!ATTLIST FILM FILMID ID #REQUIRED> <!ELEMENT TITRE (#PCDATA) >

i

i

h

i

h

h

172
<!ELEMENT <!ELEMENT <!ELEMENT <!ELEMENT <!ELEMENT AUTEUR (#PCDATA) > ANNEE (#PCDATA) > GENRE (#PCDATA) > PAYS (#PCDATA) > RESUME (#PCDATA) >

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

On voit, par ce premier exemple, que même si les DTD ne peuvent pas être exploitées directement par des programmes XSLT, elles facilitent la programmation en fournissant une description précise des données à transformer. En d’autres termes, une DTD peut servir comme système de typage complémentaire pour le langage XSLT : en utilisant un parseur validant, on vérifie alors la validité d’un document avant sa transformation. 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. Voici un autre document qui décrit une salle de cinéma : Exemple 64 Salle1.xml : Salle de cinéma
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml-stylesheet href="SalleRefOut.xsl" type="text/xsl"?> <?cocoon-process type="xslt"?> <SALLE NO=’1’ PLACES=’320’ FILMREF=’2’> <SEANCES> <SEANCE>15:00</SEANCE> <SEANCE>18:00</SEANCE> <SEANCE>21:00</SEANCE> </SEANCES> <REMARQUE>Réservation conseillée</REMARQUE> </SALLE>

1. NO : numéro de la salle ; 2. PLACES : capacité de la salle en nombre de places ; 3. FILMREF : référence vers le film projeté. Ces trois attributs sont également définis dans une DTD : Exemple 65 Salle.dtd : DTD pour les salles de cinémas
<!ELEMENT SALLE (SEANCES, REMARQUE?)> <!ATTLIST SALLE NO ID #REQUIRED PLACES CDATA #IMPLIED FILMREF NMTOKEN #REQUIRED > <!ELEMENT SEANCES (SEANCE)*> <!ELEMENT SEANCE (#PCDATA)> <!ELEMENT REMARQUE (#PCDATA)>

On peut voir que l’attribut NO est l’identificateur de la salle. 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 :

i

h

L’élément

SALLE

contient trois attributs :

4.2. SITE WEB: HTML
<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>

173

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).

4.2.5

Création de liens

Nous avons vu comment créer des pages HTML pour les séances et pour les films. Maintenant nous voudrions relier ces pages par des liens pour créer un réseau hypertexte liant ces pages. Voici un premier exemple simple de création de liens. Un fichier MesFilms.xml présente pour chaque film son identificateur, son titre mais également l’URL de la ressource qui contient plus d’information : Exemple 66 MesFilms.xml : La liste des films 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 FILM 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.

i

h

174

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

Figure 4.5: Affichage de la liste des films 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 :

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 g g g g

utilise le document MesFilms.xml comme racine ; trouve tous les éléments de type FILM dans ce document ; filtre parmi ces éléments ceux qui ont un attribut FILMREF dont la valeur est identique à la valeur de la variable filmref.

un attribut href dont la valeur est la ressource du film : $film/@HREF ; le titre du film : $film/TITRE.

4.2. SITE WEB: HTML

175

4.2.6

XML/XSLT: une solution du problème des liens cassés

Un problème connu dans la création et la maintenance d’un hypertexte de pages HTML est le problème des liens cassés (dangling links) : tous les internautes ont déjà fait l’expérience du message 404 PAGE NOT FOUND (ou un message équivalent) quand ils ont voulu suivre un lien dans une page HTML. Ce message indique que la ressource cible du lien ne peut pas être trouvée sur le serveur web interrogé. Généralement, le serveur propose soit d’aller à la page d’accueil du serveur, soit d’utiliser le bouton Back (ou Retour) pour revenir à la page d’origine. Ce problème apparaît à cause de l’absence d’un contrôle pendant la création de liens qui référencent directement des ressources physiques. Dans l’exemple précédent, nous avons vu une autre façon de gérer ce type de lien. Au lieu de référencer les ressources directement par leur URL, nous avons utilisé un fichier intermédiaire qui implante une sorte de dictionnaire traduisant des adresses logiques (généralement des identificateurs d’éléments) en adresses physiques. Le fichier MesFilms.xsl correspond à un tel dictionnaire : il indique pour chaque élément de type FILM dans quel fichier XML on peut le trouver. Afin de profiter de ce dictionnaire, on peut ensuite créer un programme XSLT qui traduit les références logiques en adresses de ressources physiques. Voici une règle XSLT qui permet de faire cette résolution : <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 correspondante. 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>

4.2.7

Intégration de pages XML

Avant de terminer cette section, nous voudrions donner un exemple d’un autre aspect important dans l’utilisation de XML pour sur la publication web. Cet aspect sera traité plus en détail dans le chapitre 6 consacré aux échanges de données. Nous avons vu comment on peut créer des liens hypertextes entre des pages XML/HTML en utilisant des références logiques entre éléments, ainsi que la fonction document() de XPath. Cette création de liens implante un certain type d’intégration de données. Si nous regardons de plus prêt, nous avons utilisé la fonction document() pour « mélanger » des données provenant de différents documents : dans l’affichage d’un lien vers un film, nous avons extrait le titre du film d’un autre document. Nous aurions également pu importer le résumé des films ou n’importe quelle autre information en utilisant le même mécanisme sous la seule contrainte qu’il existe un « lien logique » entre les ressources qui puisse être traduit en adresse physique. Une autre façon d’intégrer des ressources est l’utilisation des entités XML. Ainsi la résolution des liens revient au parseur XML qui doit remplacer des références d’entités par leur contenu. Voici un exemple d’un document qui décrit le cinéma Épée de Bois, et relie cette information aux séances dans ce cinéma : Exemple 67 EpeeCompl.xml : Cinéma Épée de Bois
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml-stylesheet href="Cinema.xsl" type="text/xsl"?> <?cocoon-process type="xslt"?> <!DOCTYPE CINEMA SYSTEM "Cinema.dtd" [ <!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> &salle1; &salle2; </CINEMA>

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>

4.3

Présentations multimédia: SMIL

SMIL est une recommandation W3C et un acronyme pour Synchronised Multimedia Integration Language. Son nom est prononcé comme le mot anglais smile, qui peut être traduit en Français par « sourire » ou « souriez ! ». Comme son nom indique, SMIL est un langage d’intégration et de synchronisation d’objets multimédia. Il permet de créer à partir d’objets numériques (texte, sons, image, vidéo) de nouveaux documents multimédia qui intègrent ces objets dans l’espace et le temps. On peut ainsi spécifier pour chaque objet non seulement sa position exacte dans le document, mais également son comportement par rapport aux autres objets et aux paramètres du média d’affichage (taux de transfert des données, langue de préférence, préférences utilisateur, etc). Pour le moment, les navigateurs web standard comme Netscape ou Internet Explorer on besoin d’une application externe capable d’interpréter les balises SMIL correctement. Parmi ces applications nous pouvons citer RealPlayer (version gratuite disponible) de RealNetworks (TM), ainsi que le produit GRiNS qui fournit un environnement de création de documents SMIL, et l’applet gratuite SOJA 1.0 Cherbourg de Helio.

4.3.1

SMIL par un exemple

Comme XHTML et WML, un document SMIL est un document XML. Nous décrivons la version 1.0 de SMIL (au moment de la rédaction de ce document, la version 2.0 de SMIL est encore au stade de la

178

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

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> <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>

Structure générale d’un document SMIL La structure générale d’un document SMIL ressemble à la structure d’un document XHTML avec un entête et un corps représentés respectivement par les balises head et body . Comme dans XHTML, l’en-tête head permet de spécifier des informations sur le document sous forme d’éléments de type meta, mais il sert surtout à spécifier les paramètres « visuels » du document multimédia par un élément de type root-layout et à découper l’espace d’affichage en plusieurs régions rectangulaires par des éléments de type region) : La fenêtre a une taille de 450x600 pixels spécifiée par les attributs width et height dans l’élément de type root-layout.

Les dimensions de la région reg_img_grand sont de 400x300 pixels et cette région est décalé de 25 points vers la droite (left) et de 25 points vers le bas (top). La valeur de l’attribut fit indique que la taille du composant affiché dans cette région doit être adaptée à la taille de la région sans déformation, c’est-à-dire en conservant le rapport largeur/longueur du composant.

La région reg_img_petit a une taille de 200x125 pixels et est déplacée de 125 pixels vers la droite et de 75 pixels vers le bas.

La région reg_texte a une taille de 350x325 pixels et est déplacée de 50 pixels vers la droite et de 250 pixels vers le bas.

i

h

i

h

k

i

h

g g g g

4.3. PRÉSENTATIONS MULTIMÉDIA: SMIL

179

reg_img_grand reg_img_petit

reg_texte

450 pixels

Figure 4.6: Placement des régions dans le document ExSimple.smil La figure 4.6 illustre le placement de ces différentes régions dans le document. L’élément de type body spécifie ensuite le contenu proprement dit, c’est-à-dire les différents composants, leur placement dans les régions d’affichage et leur synchronisation dans le temps. Les composants sont représentés par des éléments de « placement » de type audio, img et text. Ces types d’éléments correspondent aux différents types de médias utilisés dans un document multimédia. SMIL définit en tout sept types de médias (tableau 4.4). Média text image audio video textstream animation ref Utilisation composant textuel image statique composant sonore séquence vidéo texte à déroulement automatique composant animation composant d’un autre type

Table 4.4: Les différents types de média SMIL

Positionnement des composants De même qu’une image dans un document XHTML n’est pas incluse dans le document mais référencée par un élément de type img, chaque élément de placement ne contient pas le contenu du composant, mais des références sous forme d’attributs src. Néanmoins, la comparaison s’arrête là : contrairement à un élément de type img dans HTML, qui indique par sa position même le placement de l’image dans le document, un élément de placement dans SMIL spécifie explicitement le placement par une référence vers une région définie dans l’en-tête du document. Par exemple, l’image Vertigo.png est placée une première fois dans la région reg_img_grand et une deuxième fois dans la région reg_img_petit.

600 pixels

180

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

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

Attr id page1

Attr endsync first

Element audio

Element a

Element par

Attr href Sound.wav

Attr href #page2

Element img

Attr id page2

Attr dur 30s

Element text

Element img

Attr href Vertigo.png

Attr href Vertigo-Resume.txt

Attr href Vertigo.png

Figure 4.7: Arbre DOM avec éléments SMIL

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 regrouper 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 multimé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 composants 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 synchronise 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.

4.3.2

DTD SMIL

L’élément racine d’un document SMIL est de type smil et contient une en-tête de type head optionnel et un corps de type body également optionnel : <!ELEMENT smil (head?,body?)> Voici le plus petit document multimédia SMIL : <smil/> Entête : Mise-en-page

<!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 layout : 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 rassemblés par une entité générale nommé viewport-attrs : <!ENTITY % viewport-attrs " height CDATA width CDATA background-color CDATA ">

i

h

L’entête contient zéro ou plusieurs éléments switch :

meta

et au moins un élément de type layout ou de type

i

h

#IMPLIED #IMPLIED #IMPLIED

182

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

Ainsi la balise root-layout 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ésentation de 300x400 pixels : <root-layout width="300" height="400"/> Les éléments region , 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 top z-index fit >

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 :

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. sinon c’est la région avec la valeur supérieure pour l’attribut z-index qui recouvrera l’autre région.

Exemples :

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"/>

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"/>

i

i

h

h

CDATA "0" CDATA "0" CDATA "0" (hidden|fill|meet|scroll|slice) "hidden"

g g g g

4.3. PRÉSENTATIONS MULTIMÉDIA: SMIL

183

<!ELEMENT body (audio|video|text|img|animation|textstream|ref| par|seq| a| switch)*> La DTD définit le contenu d’un document par une séquence de onze types de fils différents qui peuvent être regroupés en quatre groupes :

4. les éléments de choix de qualité de service par rapport aux paramètres système du réseau et du client : switch Voici la définition plus détaillée de ces différents éléments. É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 region IDREF src CDATA dur CDATA repeat CDATA fill (remove|freeze) begin CDATA #IMPLIED end CDATA #IMPLIED "> #IMPLIED #IMPLIED #IMPLIED #IMPLIED ’1’ ’remove’

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) ;

id : permet d’identifier l’élément de positionnement ;

region : spécifie la région où le contenu sera affiché ;

repeat : le nombre de répétitions de l’affichage (par exemple pour un fichier audio, qui peut être joué en boucle) ;

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).

dur : la durée d’affichage ;

begin : le début d’affichage ;

i h

3. les liens hypertextes:

a ;

i

h

i

h

2. les éléments de synchronisation

seq

et

par ;

i

h i

h i

h i

h i

h

1. les éléments de positionnement: textstream , ref ;

audio ,

video ,

text ,

i

h

Le contenu d’une page SMIL est spécifié par le contenu de l’élément

i

h i

i

i

h

Élément

body : contenu body .

img ,

animation ,

h h

g g g g g g g

184

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

Le fichier texte Alien-Info.txt est affiché dans la région reg_texte2 pendant Exemple 71 5 secondes : <text src="Alien-Info.txt" region="reg\_texte2" dur="5s"/>

L’extrait video du film est affiché dans la région reg_video1 par un élément de placement clip de type video. La dernière image restera figée sur l’écran après la fin de l’extrait : <video id="clip" src="Alien-Clip.mov" region="reg\_video1" fill="freeze"/>

Le résumé du film sera affiché dans la region reg_texte3 au début de la vidéo présentée par l’élément de placement avec l’identificateur clip : <text src="Alien-Resume.txt" region="reg\_texte3" begin="id(clip)(begin)"/>

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 clip-end CDATA "> #IMPLIED #IMPLIED

Exemple 72 L’extrait affiché du clip Alien-Clip.mov commence 5 secondes après le début : <video src="Alien-Clip.mov" region="reg\_video1" clip-begin="5s" fill="freeze"/> On peut également observer que l’attribut fill est égal à freeze, ce qui signifie que la dernière image du clip restera affichée après la fin. Éléments de 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 :

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.

dur : spécifie la durée du groupe en temps réel (en heures, minutes et/ou secondes). Cela correspond à une synchronisation par horloge ;

repeat : indique combien de fois le groupe doit être affiché (boucle) ;

i

h

Les balises par et seq ont le même modèle de contenu que l’élément l’utilisation d’attributs supplémentaires pour la synchronisation :

i

h

i

g

h

g g g g g g

end : la fin d’affichage.

body , mais permettent

4.3. PRÉSENTATIONS MULTIMÉDIA: SMIL

185

Exemple 73 Le groupe parallèle de deux éléments audio et img 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

Attr endsync first

Element audio

Element img

Attr src Sound.wav

Attr src Alien.png

Attr region reg_img_grand

Figure 4.8: Arbre DOM avec éléments SMIL

Exemple 74 Voici un exemple un peu plus compliqué : <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>

i

h

i

h

g g

region : indique la région d’affichage ; begin et ends : indique le début et la fin de l’affichage. Ces valeurs peuvent être indiquées en durée réelle par rapport au début du parent (synchronisation par horloge) ou par une expression id(ref)(begin) ou id(ref)(end) où ref correspond à un autre élément du contenu (synchronisation par événement).

186
Element seq

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

Element par

Element par

Attr endsync first

Element audio

Element a

Attr id page2

Element text

Element text

Element text

Element seq

Attr href #page2

Element img

Element img

Element img

Figure 4.9: Arbre DOM avec éléments SMIL 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.

Figure 4.10: Déroulement d’une séquence SMIL

Exemple 75 Par exemple, l’élément switch suivant prend en compte la taille de l’écran pour choisir entre trois versions à différentes résolutions d’une même image ;

i

h

i

h

La balise

switch

permet de prendre en compte les paramètres du réseau et du client d’affichage.

i

h

Paramètres d’environnement:

switch

4.3. PRÉSENTATIONS MULTIMÉDIA: SMIL
<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> Si aucun paramètre peut être satisfait, le texte sorry.txt sera affiché.

187

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.

4.3.3

Génération de présentations SMIL

Comme nous l’avons déjà souligné, l’intérêt du couple XML/XSLT est la possibilité de publier le contenu dans un document XML sous différentes formes. Pourquoi pas créer alors une présentation SMIL à partir du contenu d’un document XML. À priori cette tâche semble similaire à la génération de pages HTML décrite dans la section 4.2. Si on y regarde de plus près, on remarque quelques différences importantes entre SMIL et HTML :

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 ; Exemple 76 SleepyHollow.xml : Fichier XML pour le film Sleepy Hollow
<?xml version="1.0" encoding="ISO-8859-1"?> <FILM FILMID=’3’> <TITRE>Sleepy Hollow</TITRE> <AUTEUR>Tim Burton</AUTEUR> <ANNEE>1999</ANNEE> <GENRE>Fantastique</GENRE> <PAYS>Etats Unis</PAYS> <RESUME> Nouvelle Angleterre, 1799. A Sleepy Hollow, plusieurs cadavres sont retrouvés décapités. La rumeur attribue ces meurtres à un cavalier lui-même sans tête. Mais le fin limier new-yorkais Ichabod Crane ne croit pas en ses aberrations. Tombé sous le charme de la vénéneuse Katrina, il mène son enquête au coeur

g g

Un SMIL est ne contient pas les données affichées à l’écran, mais sert comme cadre de présentation pour des données qui se trouvent dans d’autres ressources. Dans ce sens, il est préférable d’utiliser le terme présentation SMIL à la place de document. Un document XML n’est alors pas simplement transformé en un autre document conforme à la DTD SMIL, mais doit fournir les composants qui seront affichés par la présentation SMIL. Une présentation SMIL inclut généralement des composants qui ne sont pas dans un format XML et le document XML ne correspond généralement qu’à une partie des composants de la présentation.

188
des sortilèges de Sleepy Hollow.. </RESUME> </FILM>

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

Voici la présentation SMIL que nous voudrions créer : Exemple 77 SleepyHollow.smil : Fichier SMIL pour la présentation du film Sleepy Hollow
<?xml version="1.0" encoding="utf-8"?> <smil> <head> <meta name="title" content="Presentation SMIL du film Sleepy HÃ c llow"/> <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> <par> <text src="Sleepy-Title.txt" region="reg_texte1" fill="freeze"/> <text src="Sleepy-Info.txt" region="reg_texte2"/> <text src="Sleepy-Resume.txt" region="reg_texte3" begin="2s"/> <seq> <img src="IMG/mmedia_photo.png" region="reg_img_petit" dur="5s"/> <img src="IMG/2_1tn.png" region="reg_img_petit" dur="5s"/> <img src="IMG/2_2tn.png" region="reg_img_petit" dur="5s"/> <img src="IMG/2_3tn.png" region="reg_img_petit" dur="5s"/> </seq> </par> </body> </smil>

Cette présentation contient trois composants de type Text :

Sleepy-Title.txt contient le tire du film ;

Sleepy-Info.txt contient l’auteur, l’année, le genre et le pays ;

Sleepy-Resume.txt contient le résumé du film.

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 : Exemple 78 Sleepy-Title.txt : Fichier avec le titre du film
<window type="marquee" height="50" width="200" bgcolor="black"> <font size="50" face="Garamond" color="red">Sleepy Hollow</font> </window>

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.

k

g g g

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 :

font : permet de spécifier des caractéristiques de la police utilisée pour le texte qu’elle contient (couleur, taille, etc.). a : 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 center , br et p 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 horizontalement de droite à gauche. RealText distingue entre cinq type d’affichage d’un texte : generic : affichage générique sans paramètres prédéfinis ;

marquee : le texte se déroule horizontalement (de droite à gauche par défaut) et est positionné au milieu de la fenêtre ;

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 ;

scrollingnews : le texte se déroule verticalement (du bas vers le haut par défaut) ;

teleprompter : le texte est affiché comme dans l’affichage générique mais le nouveau texte « pousse » l’ancien vers le haut.

Voici le fichier RealText pour l’affichage des informations sur le film : Exemple 79 Sleepy-Info.txt : Information sur le film Vertigo

<window type="marquee" height="50" width="200" bgcolor="black" link="blue"> <font color="white"> de Tim Burton, Fantastique, Etats Unis, 1999, <a href="http://www.sallesenligne.com/films/Slee low</a> </font> </window>

Les informations se déroulent également de droite à gauche. Il contient également un lien vers le fichier SleepyHollow.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
<window type="scrollingnews" height="100" width="250" bgcolor="black"> <font color="yellow">

i h

i

h i

h

i

i

i h g

h g h g g g g g g

window : 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...

190

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

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...

</font> </window>

Figure 4.11: Copie d’écran de la présentation SleepyHollow.smil

Génération de composants RealText La génération de ces différents composants RealText n’a plus de secret pour les programmeurs XSLT expérimentés que nous sommes maintenant. Voici la première règle pour la génération du fichier Sleepy-Title.txt ; <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
</window> </xsl:template>

191

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 : <window type="marquee" height="50" width="200" bgcolor="black" link="blue"> <font color="white"> de Tim Burton, Fantastique, Etats Unis, 1999 </font> </window> 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> On a réduit les espaces blancs en faveur de la lisibilité du programme XSLT. 2. La deuxième est d’exploiter le fait que XSLT supprime tous les noeuds de type Text qui ne contiennent qu’un espace blanc. Ainsi, en séparant les virgules après chaque champs de l’espace blanc qui suit, ces espaces seront supprimés. Pour cela, on utilise l’instruction xsl:text : <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 xsl:text et xsl:value seront supprimés pendant la sérialisation de l’arbre résultat. Nous allons détailler l’instruction xsl:text 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 : <xsl:output method="xml" encoding="ISO-8859-1" /> Éclatement d’un document XML Bien que nous ayons supposé que chaque fichier RealText est généré par un programme XSLT différent, on peut définir également un seul programme avec des paramètres qui permettent de choisir la transformation souhaitée. Néanmoins, on est obligé de faire pour chaque fichier une demande de transformation, car la version 1.0 de XSLT ne permet de produire qu’un seul document par transformation. Ceci n’est pas un problème dans un environnement où on peut créer des scripts ou programmes simples pour automatiser ce processus, mais ça le devient rapidement. La version 1.1 de XSLT et la plupart des processeurs XSLT actuels (qui sont conformes à la version 1.0) proposent des extensions pour pouvoir créer plusieurs documents par une seule demande de transformation. Cette extension s’appelle xsl:document (page 266), saxon:output dans le processeur Saxon et xalan:rewrite dans le processeur Xalan. Ainsi il est possible de définir des règles dont résultat n’est pas inclus dans l’arbre (et le document) produit par la feuille de style mais stocké dans des fichiers différents. Voici un exemple basé sur le processeur XSLT Saxon. La règle crée deux fichiers RealText {$prefix}Titre.txt et {$prefix}Info.txt où $prefix est un paramètre de règle : <xsl:template name="genTitreInfo"> <xsl:param name="prefix" value="unknown.txt"/> <saxon:output file="{$prefix}Titre.txt" omit-xml-declaration="yes" > <window type="marquee" height="50" width="200" bgcolor="black"> <font size="50" face="Garamond" color="red"> <xsl:value-of select="TITRE"/> </font> </window> </saxon:output> <saxon:output file="{$prefix}Info.txt" omit-xml-declaration="yes" >

i

i

h

h

i

h

4.3. PRÉSENTATIONS MULTIMÉDIA: SMIL
<window type="marquee" height="50" width="200" bgcolor="black" link="blue" indent="no"> <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> </saxon:output> </xsl:template>

193

Les attributs de l’instruction saxon:output sont essentiellement les mêmes que les attributs de l’élément du premier niveau xsl:output. Génération du fichier SMIL Après avoir généré les différents composants RealText à partir du contenu du document XML SleepyHollow.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 : Exemple 81 ParamsSMIL.xml : Paramètres pour la génération de la présentation
<?xml version="1.0" encoding="ISO-8859-1"?> <SMILPARAMS> <PARAM FILMREF="1" PREFIX="Vertigo-" RESOURCE="Vertigo.xml"> <PHOTOS> <PHOTO HREF="IMG/Vertigo1.png"/> <PHOTO HREF="IMG/Vertigo2.png"/> </PHOTOS> </PARAM> <PARAM FILMREF="2" PREFIX="Alien-" RESOURCE="Alien.xml" /> <PARAM FILMREF="3" PREFIX="Sleepy-" RESOURCE="http://www.sallesenligne.com/films/SleepyHollow.xml"> <PHOTOS> <PHOTO HREF="IMG/mmedia_photo.png"/> <PHOTO HREF="IMG/2_1tn.png"/> <PHOTO HREF="IMG/2_2tn.png"/> <PHOTO HREF="IMG/2_3tn.png"/> </PHOTOS> </PARAM> </SMILPARAMS>

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 : Exemple 82 GenSMIL.xsl : Feuille de style pour la génération du document SMIL

194

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

<?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:strip-space elements="*"/> <xsl:output method="xml" indent="yes" media-type="text/smil"/> <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>

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

i

h

g g

La règle <xsl:template match="/"> génère l’entête du document et applique ensuite la transformation pour l’élément FILM .

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 document 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 a de XHTML.

4.4

Traitement de texte dans XSLT

Nous allons présenter plus en détail les éléments de premier niveau et les instructions XSLT pour le traitement des noeuds de type Texte qui apparaissent dans le document à transformer, mais également dans les programmes XSLT. Le terme traitement comprend plusieurs fonctions qui interviennent pendant la transformation d’un document et qui sont illustrées dans la figure 3.1 (page 110 du chapitre 3) :

analyse du document source et du programme XSLT par le parseur XML : cette phase a été décrite dans la section 2.3 ;

génération de l’arbre XPath du document source : nous avons vu page 81 que XPath et donc XSLT connaît seulement un sous-ensemble des types DOM et que les noeuds de type CDataSection sont tous transformés en noeuds de type Text ;

suppression éventuelle de noeuds de texte « blancs » : l’arbre XPath du document source contient généralement un grand nombre de noeuds de type Text qui ont comme valeur un espaces (un exemple a été étudié dans la section 2.3). La plupart de ces espaces sont non-significatifs et servent uniquement pour le formattage du document sérialisé. Mais ce qui est plus important est que ces espaces ne sont pas toujours neutres par rapport au format du document à produire. Par exemple nous avons vu que pour la production d’un document HTML, on peut souvent ignorer la présence d’espaces dans le document source. Par contre, dans d’autres cas, ils sont un effet indésirable et doivent être supprimés. XSL permet de contrôler le traitement d’espaces dans le document source par deux éléments de premier niveau, xsl:desc-preserve et xsl:desc-strip, qui seront décrits plus loin.

transformation du document : la phase de transformation de l’arbre XPath permet uniquement de créer des noeuds de type Text en mélangeant des noeuds de type Text : – obtenus comme résultat d’une expression XSLT ; – contenux dans le corps des règles XSLT ;

sérialisation du document : après la transformation on obtient un arbre XPath qui est ensuite sérialisé pour obtenir un document XML qui peut être stocké dans un fichier ou échangé avec une autre application. Cette phase de sérialisation peut être contrôle par l’élément xsl:output décrit plus loin.

4.4.1

xsl:preserve-space et xsl:strip-space

Ces deux éléments XSLT de premier niveau permettent de contrôler la suppression des espaces blancs dans le document source avant de commencer la transformation. Le niveau de contrôle est le type de l’élément qui contient le noeud d’espace : il est possible de décider pour tous les éléments d’un type s’il faut supprimer (xsl:strip-space) ou préserver (xsl:preserve-space) les fils d’espace. Les

i h

g g g g g

196

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

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 #x20

Element B

Text #x20 c

Text #x20 #x20

Element C

Text #x20

Element D

Element D

Text #x20 b #x20

Text #x20 #x20

Text a #x20

Figure 4.12: Arbre XPath

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 D 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).

i h

4.5. RSS ET RDF
Attribut xml:space

197

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 pre de la DTD XHTML. L’attribut xml:space peut prendre deux valeurs : default ou preserve. La première valeur indique 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 : il n’a pas d’élément ancêtre avec un attribut xml:space ou

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>

4.4.2
TODO

Génération de texte : xsl:text

4.4.3
TODO

Sérialisation du résultat : xsl:output

4.5

RSS et RDF

Va dans le chapitre « Échange et Integration » : c’est à ça que ça sert... ;

i

h

g g

198

CHAPTER 4. PRODUCTION DE DOCUMENTS XML

Chapter 5

Production de documents papier
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

Publication de bases de données
Sommaire
7.1 Bases de données et XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 7.1.1 7.1.2 7.1.3 7.1.4 7.2 7.2.1 7.2.2 7.2.3 7.3 7.3.1 7.3.2 7.4 Quelques rappels sur les BD relationnelles . . . . . . . . . . . . . . . . . . . . 204 Documents orientés « texte » et documents orientés « données » . . . . . . . . . 207 Transformation d’une base de données en XML . . . . . . . . . . . . . . . . . 209 Création de la DTD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 Une classe Java d’exportation XML . . . . . . . . . . . . . . . . . . . . . . . . 218 Architecture Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 Utilisation des Java Server Pages . . . . . . . . . . . . . . . . . . . . . . . . . 225 XSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 XSQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235

Architectures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218

XML dynamique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230

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 information 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 composants 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 produire 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.

7.1

Bases de données et XML

Plaçons-nous pour l’instant dans la situation où des informations stockées dans une base de données doivent être converties en document(s) XML. Nous effectuons un rappel des principales caractéristiques du modèle relationnel avant de décrire le processus de conversion.

7.1.1

Quelques rappels sur les BD relationnelles

Prenons l’exemple (très simplifié) de la base de données d’un organisme de voyage. Cet organisme propose des séjours (sportifs, culturels, etc) se déroulant dans des stations de vacances. Chaque station propose un ensemble d’activités (ski, voile, tourisme). Enfin l’organisme souhaite gérer une liste des clients (avec le solde de leur compte !) et des séjours auxquels ils ont participé avec leurs dates de début et de fin. La première chose à faire est d’établir un schéma décrivant comment les informations nécessaires vont être stockées dans la base. La conception d’un schéma relationnel n’entre pas dans le cadre de ce livre. Il suffira de dire qu’il est constitué d’un ensemble de schémas de tables (ou relation), chaque table étant décrite par un ensemble d’attributs. Voici une représentation concise du schéma pour notre agence de voyages.

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.

g g g g

Station (nomStation, capacité, lieu, région, tarif) Activité (nomStation, libellé, prix) Client (id, nom, prénom, ville, région, solde) Séjour (idClient, station, début, nbPlaces)

7.1. BASES DE DONNÉES ET XML
id 10 20 30 nom Fogg Pascal Kerouac idClient 10 30 20 30 30 20 30 10 prénom ville Phileas Londres Blaise Paris Jack New York La table Client région Europe Europe Amérique nbPlaces 2 5 4 3 3 6 5 3 solde 12465 6763 9812

205

station début Passac 2001-07-01 Santalba 2001-08-14 Santalba 2001-08-03 Passac 2001-08-15 Venusa 2001-08-03 Venusa 2001-08-03 Farniente 2002-06-24 Farniente 2002-09-05 La table Séjour

Figure 7.1: Les clients et leurs séjours Une clé étrangère est un (ou plusieurs) attribut(s) dans une table dont la valeur fait référence à une valeur de clé primaire dans une table . 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 : 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 ;

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 principales 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 codification 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). Exemple 84 Schema.sql : Création du schéma de la base

l

m

g g

206 nomStation Venusa Farniente Santalba Passac

CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
capacité 350 200 lieu Guadeloupe Sicile Martinique 400 Alpes La table Station région Antilles Europe Antilles Europe prix 150 130 200 20 50 tarif 1200 1500 2000 1000

nomStation libellé Venusa Voile Venusa Plongée Farniente Plongée Passac Ski Passac Piscine Santalba Kayac La table Activité

Figure 7.2: Les stations et leurs activités

# # Schéma de la base ’Agence de voyages" # CREATE TABLE Station (nomStation VARCHAR (30) NOT NULL, capacite INTEGER, lieu VARCHAR (30) NOT NULL, region VARCHAR (30) NOT NULL, tarif DECIMAL (10,2), PRIMARY KEY (nomStation), CONSTRAINT nom_region CHECK (region IN (’Antilles’, ’Europe’, ’Amérique’, ’Asie’,’Afrique’)) ); CREATE TABLE Activite (nomStation VARCHAR (30) NOT NULL, libelle VARCHAR (30) NOT NULL, prix DECIMAL (10,2), PRIMARY KEY (nomStation, libelle), FOREIGN KEY (nomStation) REFERENCES Station ); CREATE TABLE Client (id INTEGER NOT NULL, nom VARCHAR (30) NOT NULL, prenom VARCHAR (30) NOT NULL, ville VARCHAR (30) NOT NULL, region VARCHAR (30) NOT NULL, solde DECIMAL (10,2) NOT NULL, PRIMARY KEY (id) ); CREATE TABLE Sejour (idClient INTEGER NOT NULL, station VARCHAR (30) NOT NULL, debut DATE NOT NULL, nbPlaces INTEGER NOT NULL, PRIMARY KEY (idClient, station, debut), FOREIGN KEY (idClient) REFERENCES Client, FOREIGN KEY (station) REFERENCES Station);

7.1. BASES DE DONNÉES ET XML

207

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 : 1. la base est décrite par un schéma séparé (physiquement) des données ; 2. le schéma est constitué de descriptions de tables (ou relations) ; 3. les caractéristiques d’une table sont : (a) son nom ; (b) le nom de ses attributs (ou colonnes) ; (c) le ou les attribut(s) formant la clé primaire ; (d) une ou plusieurs clés étrangères, chacune constituée d’un ou plusieurs attributs ; (e) le type de chaque attribut, et la contrainte NOT NULL ; (f) les contraintes d’appartenance à un ensemble énuméré. 4. l’ordre des lignes ou des colonnes d’une table n’est pas significatif. Voyons maintenant comment un schéma relationnel trouve son équivalent en XML.

7.1.2

Documents orientés « texte » et documents orientés « données »

L’exportation de bases de données donne des documents XML assez particuliers. Au départ, XML est un dérivé de SGML destiné à faciliter l’échange et la représentation de documents numériques (documentation technique, livres, maquettes, etc). De tels documents sont caractérisés par l’absence de typage (tout est vu comme une chaîne de caractères), une forte présence de texte descriptif, et une structure très souple. Par contraste, une base de données est très structurée, décrit l’information avec une granularité très fine, et type cette information. Voici une représentation possible en XML de la table Station. La racine du document est de type Station. On trouve ensuite un élément pour chaque ligne, et dans cet élément autant d’éléments qu’il y a d’attributs. Exemple 85 Station.xml : Une représentation XML de la table Station
<?xml version=’1.0’ encoding=’ISO-8859-1?> <Stations> <Station> <nomStation>Venusa</nomStation> <capacite>350</capacite> <lieu>Guadeloupe</lieu> <region>Antilles</region> <tarif>1200.00</tarif> </Station> <Station>

208
<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>

CHAPTER 7. PUBLICATION DE BASES DE DONNÉES

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 descriptifs 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érations, 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. Exemple 86 AvantPropos.xml : Un document orienté « texte » 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. Exemple 87 Promotion.xml : Un document mixte

7.1. BASES DE DONNÉES ET XML
<?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 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>

209

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 exemple 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.

7.1.3

Transformation d’une base de données en XML

Nous étudions maintenant de manière systématique la transformation de tout ou partie d’une base de données relationnelle en document XML. É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 Stations ). 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.

i

h

210

CHAPTER 7. PUBLICATION DE BASES DE DONNÉES

Exemple 88 StationAttrs.xml : Réprésentation de Station avec des attributs
<?xml version=’1.0’ encoding=’ISO-8859-1?> <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 :

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. Représentation des associations entre tables Passons maintenant à la représentation XML des liens entre les tables. En relationnel, nous avons vu que les liens sont définis par une correspondance entre la clé primaire dans une table, et une clé étrangère

g 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) ; 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.

7.1. BASES DE DONNÉES ET XML

211

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 document), mais on a conservé le critère de rapprochement. Exemple 89 StationActivite.xml : Stations et activités
<?xml version=’1.0’ encoding=’ISO-8859-1?> <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 correspondance 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 Activite 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. Exemple 90 StationImbriquee.xml : Représentation avec imbrication
<?xml version=’1.0’ encoding=’ISO-8859-1?> <Stations> <Station nomStation=’Venusa’ capacite=’350’ lieu=’Guadeloupe’ region=’Antilles’ tarif=’1200.00’> <Activite libelle=’Voile’ prix=’150.00’/> <Activite libelle=’Plongee’/> </Station> <Station nomStation=’Farniente’ capacite=’200’ lieu=’Seychelles’ region=’Océan Indien’ tarif=’1500.00’> <Activite libelle=’Plongée’ prix=’130.00’/> </Station> </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 informations 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 Activite ) est lié à un et un seul autre (ici Station ). 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

i

h

i

h

i

h

7.1. BASES DE DONNÉES ET XML
<?xml version=’1.0’ encoding=’ISO-8859-1?> <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>

213

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. Exemple 92 StationSejourClient.xml : Stations, séjours et clients
<?xml version=’1.0’ encoding=’ISO-8859-1?> <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.

7.1.4

Création de la DTD

Quand on crée un outil exportant une base de données (ou une partie d’une base de données) dans un document XML, on peut, en plus du document répresentant le contenu de la base, créer la DTD. L’intérêt est de pouvoir fournir aux utilisateurs une description de la structure qui ne dépende pas d’une instance particulière de la base. La création d’une DTD permet également de reconstituer en partie les contraintes du schéma relationnel, Nous continuons à prendre l’exemple de notre base « Agence de voyages », en supposant les choix suivants : 1. les colonnes sont représentées par des attributs XML ; 2. le chemin d’accès principal est la station ; 3. pour chaque station on trouve, imbriqués, les séjours de la station, et dans chaque séjour les clients qui ont séjourné dans la station ; 4. pour les besoins de la présentation, on va supposer que les activités de la station sont représentés par des éléments indépendants, avec un lien de navigation. Un exemple de document conforme à ce schéma est StationSejourClient.xml, page 214, auquel on pourrait ajouter des éléments Activite comme fils de l’élément racine. Racine du document Les documents auront un élément racine de type Stations, constitué de 0 ou plusieurs éléments de type Station. <!ELEMENT Stations (Station*)> Description de la table Station Rappelons le schéma relationnel de la table Station. Nous allons essayer de nous y conformer dans la DTD. CREATE TABLE Station (nomStation capacite lieu region tarif VARCHAR (30) NOT NULL, INTEGER, VARCHAR (30) NOT NULL, VARCHAR (30) NOT NULL, DECIMAL (10,2),

i

h

216

CHAPTER 7. PUBLICATION DE BASES DE DONNÉES
PRIMARY KEY (nomStation), CONSTRAINT nom_region CHECK (region IN (’Antilles’, ’Europe’, ’Amérique’, ’Asie’,’Afrique’)) );

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 Station 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 Station peut avoir comme fils 0 ou plusieurs éléments Sejour . Voici la partie de la DTD correspondant à la table Station. <!ELEMENT Station (Sejour*)> <!ATTLIST Station nomStation ID #REQUIRED capacite CDATA #IMPLIED lieu CDATA #REQUIRED tarif CDATA #REQUIRED region (Antilles Europe Amérique Asie) #REQUIRED > Tables Séjour et Client Voici la commande de création de la table Séjour. La clé est constituée de plusieurs attributs, et on ne peut donc pas la représenter avec la DTD. CREATE TABLE Sejour (idClient INTEGER NOT NULL, station VARCHAR (30) NOT NULL, debut DATE NOT NULL, nbPlaces INTEGER NOT NULL,

i

h

i

h

i

h

7.1. BASES DE DONNÉES ET XML
PRIMARY KEY (idClient, station, debut), FOREIGN KEY (idClient) REFERENCES Client, FOREIGN KEY (station) REFERENCES Station);

217

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 Sejour étant par ailleurs fils d’un et un seul élément Station . <!ELEMENT Sejour (Client)> <!ATTLIST Sejour debut CDATA #REQUIRED nbPlaces CDATA #REQUIRED > 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. CREATE TABLE Activite (nomStation VARCHAR (30) NOT NULL, libelle VARCHAR (30) NOT NULL, prix DECIMAL (10,2), PRIMARY KEY (nomStation, libelle), FOREIGN KEY (nomStation) REFERENCES Station ); On ne peut pas déclarer d’identifiant puisqu’il est composé de deux colonnes. En revanche il est possible 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 Activite a une valeur égale à celle de l’attribut correspondant dans un élément Station . Contrairement au schéma de la table relationnelle, rien n’indique que nomStation dans Activite fait référence à nomStation dans Station puisque les identifiants sont globaux au document. Voici pour conclure la DTD complète. Exemple 93 BaseStation.dtd : La DTD de la base
<!-- DTD des documents exportés de la base "Agence de voyages --> <!ELEMENT Stations (Station*)>

i

i

i

h

h

h

i

i

h

h

i

h

218

CHAPTER 7. PUBLICATION DE BASES DE DONNÉES

<!ELEMENT Station (Sejour*)> <!ATTLIST Station nomStation ID #REQUIRED capacite CDATA #IMPLIED lieu CDATA #REQUIRED tarif CDATA #REQUIRED region (Antilles Europe Amérique Asie) #REQUIRED > <!ELEMENT Sejour (Client)> <!ATTLIST Sejour debut CDATA #REQUIRED nbPlaces CDATA #REQUIRED > <!ELEMENT Client EMPTY> <!ATTLIST Client id CDATA #REQUIRED nom CDATA #REQUIRED prenom CDATA #REQUIRED ville CDATA #REQUIRED region CDATA #REQUIRED solde CDATA #REQUIRED > <!ELEMENT Activite EMPTY> <!ATTLIST Activite nomStation IDREF #REQUIRED libelle CDATA #REQUIRED prix CDATA #IMPLIED >

7.2

Architectures

Nous allons maintenant étudier quelques architectures-types permettant d’intégrer dans un système de publication 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 (separation 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.

7.2.1

Une classe Java d’exportation XML

Commeçons par créer une classe Java qui exporte le résultat d’une requête en suivant les principes exposés dans la section précédente. Cette classe doit offrir les fonctionnalités suivantes :

7.2. ARCHITECTURES
1. connexion à n’importe quelle base de données ; 2. exécution d’une requête ;

219

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(), formatElements() et formatAttributs() étant décrites par la suite.
public class ExportXML { static Connection connexion; public ExportXML(String driver, String nomBase, String login, String password) throws SQLException, ClassNotFoundException

{ // Chargement du driver Class.forName (driver); // Connection à la base connexion = DriverManager.getConnection (nomBase, login, password); } public String formatXML (String requete, String nomElement, String mode) { ... } // Formatage avec éléments private String formatElements (String nomElement, ResultSet resultat, ResultSetMetaData schema) throws SQLException { ... } // Formatage avec attributs private String formatAttributs (String nomElement, ResultSet resultat, ResultSetMetaData schema) throws SQLException { ... } }

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 comprendre 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(); // Sortie du résultat avec balises XML while (resultat.next ()) { if (mode.equals("elements")) { chaineXML.append(formatElements(nomElement, resultat, schema)); } else { chaineXML.append(formatAttributs(nomElement, resultat, schema)); } } resultat.close(); } catch (SQLException e) { chaineXML.append("<ERREUR>" + e.getMessage() +"</ERREUR>\n"); } 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
ResultSetMetaData schema) throws SQLException { StringBuffer chaine = new StringBuffer(); String valeur, nom ; int i, nbAttrs; // On prend le nombre d’attributs nbAttrs = schema.getColumnCount(); chaine.append("<" + nomElement + " "); for (i=1; i <= nbAttrs; i++) { nom = schema.getColumnName(i); valeur = resultat.getString(i); if (!resultat.wasNull()) { chaine.append(nom + "=’" + valeur + "’ \n"); } } chaine.append("/>\n"); return chaine.toString(); }

221

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. Exemple 94 Extracteur.java : Programme d’extraction d’une base de données
/* Ce programme fait appel à la classe ExportXML pour formatter * le résultat d’une requête SQL en XML (en mode attributs). * Il prend trois arguments: * 1 - La requête SQL sur la table Station * 2 - Le nom de l’élément pour chaque ligne * 3 - Le nom de l’élément racine du document * Exemple: java Extracteur ’select * from Station’ ’STATION’ ’STATIONS’ * * NB: ceci est un programme de démonstration: * aucun contrôle n’est effecté sur les paramètres */ // Import des classes Java

222
import java.util.*; import ExportXML;

CHAPTER 7. PUBLICATION DE BASES DE DONNÉES

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"); // Elément racine du document System.out.println ("<" + args[2] + ">\n"); // Mise en forme du résultat System.out.println(export.formatXML (args[0], args[1], "attributs")); // Fin de l’élément racine System.out.println ("</" + args[2] + ">\n"); } catch (Exception e) { System.out.println ("Impossible de se connecter à MySQL"); System.out.println (e.getMessage()); } } }

L’utilisation d’un programme comme celui-ci, combiné à des transformations XSLT pour créer par exemple 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.

7.2.2

Architecture Servlet

La première solution permettant de générer dynamiquement du XML en fonction de demandes utilisateurs est une architecture servlet. Une servlet est un objet Java intégré à un serveur web qui prend en charge des requêtes HTTP (figure 7.3). cet objet dispose de toutes les possibilités du langage Java, et en particulier de la possibilité de se connecter à une base de données via JDBC. Nous allons prendre l’exemple d’une servlet qui reçoit des requêtes SQL sur la base Station et y répond sous forme d’un document XML. Pour l’instant nous n’introduisons pas de transformation XSLT. La figure 7.4 montre le formulaire HTML qui permet d’interroger la servlet. Comme dans le cas du programme Extracteur.java ci-dessus, le formulaire envoie la requête, ainsi que des paramètres de création du document : nom de l’élément racine, nom de l’élément pour chaque ligne, mode de présentation (attributs ou éléments). Au lieu de répondre comme d’habitude par un document HTML mis en page par le navigateur, la servlet renvoie un document XML que l’utilisateur peut choisir soit de stocker, soit de mettre en forme avec un programme XSLT ou une feuille de style CSS. Nous verrons un peu plus loin comment on peut,

7.2. ARCHITECTURES

223

requêtes HTTP document(s) HTML

Serveur web Machine Java

Request Servlet Response
JDBC

BD

Machine serveur

Figure 7.3: Archictecture avec servlet 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. Exemple 95 ServletXML.java : La servlet d’extraction XML
import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import ExportXML; public class ServletXML extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { StringBuffer chaineXML = new StringBuffer(); String requete, element, mode, racine; ExportXML export; response.setContentType("text/plain"); PrintWriter out = response.getWriter(); // Récupération des paramètres requete = request.getParameter("requete"); mode = request.getParameter("mode"); racine = request.getParameter("racine"); element = request.getParameter("element"); 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

Figure 7.4: Un formulaire d’interrogation de servlet
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 fonctionnalité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

Figure 7.5: Le résultat de l’interrogation 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.

7.2.3

Utilisation des Java Server Pages

Les Java Server Pages constituent une extension des servlet dont l’objectif principal est de faciliter la création de documents combinant des parties statiques et des parties dynamiques. Un des principaux inconvénients des servlets est en effet la nécessité de produire tout le résultat en faisant appel, pour chaque

226

CHAPTER 7. PUBLICATION DE BASES DE DONNÉES

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) : 1. la couche contenu/présentation utilise un langage avec balise qui uniformise (relativement...) la manipulation des parties statiques et des parties dynamiques ; 2. la couche application est constituée d’objets qui fournissent les services de calcul, mise en forme, transformation des données ; 3. enfin la couche données constitue le stockage des informations de l’application. La figure 7.6 montre cette architecture, la couche applicative étant représentée par un composant Java Bean.

Serveur web

JDBC JSP Représentation XML JavaBean Objet intermédiaire

BD Contenu/Présentation Application Données

Figure 7.6: Architecture avec JSP et un java bean 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> <%@ page import="java.io.*,java.util.*" %> Date : <%= new Date()%> </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

select * from Station where nomStation=’nom’ Nous avons besoin d’une classe intermédiaire (un « bean » Java) qui va prendre en charge les échanges entre la page JSP et la classe ExportXML.java. Voici le code de cette classe : Exemple 97 XMLBean.java : Un JavaBean associé à une page JSP
// Bean Java pour page JSP import ExportXML; public class XMLBean { private StringBuffer chaineXML = new StringBuffer(); private String nomStation, element, mode; private ExportXML export; 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"; } public void setNomStation(String s) {nomStation = s;} public void setElement(String e) {element = e;} public void setMode(String m) {mode = m;} public String getChaineXML() { chaineXML.append (export.formatXML ("select * from Station where nomStation=’" + nomStation + "’", element, mode)); return chaineXML.toString(); } }

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é. Exemple 98 RequeteXML.jsp : La page JSP effectuant la mise en page de la requête
<% response.setContentType("text/plain"); %> <%@ page import="java.io.*,java.util.*,XMLBean" %> <jsp:useBean id="monBean" class="XMLBean"/>

228

CHAPTER 7. PUBLICATION DE BASES DE DONNÉES

<jsp:setProperty name="monBean" property="nomStation"/> <jsp:setProperty name="monBean" property="element"/> <jsp:setProperty name="monBean" property="mode"/> <?xml version=’1.0’ encoding=’ISO-8859-1’?> <<%= request.getParameter("racine") %>> <jsp:getProperty name="monBean" property="chaineXML"/> </<%= request.getParameter("racine") %>>

On utilise, de manière transparente, les fonctionnalités du bean avec des instructions JSP : 1. l’instruction jsp:useBean id="monBean" class="XMLBean"/ 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 jsp:setProperty name="monBean" property="nomStation"/ revient à 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 ;

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"?> <% response.setContentType("text/plain"); %> <%@ page import="java.io.*,java.util.*,XMLBean" %> <jsp:useBean id="monBean" class="XMLBean"/> <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> <jsp:setProperty name="monBean" property="nomStation" value="Passac"/> <jsp:getProperty name="monBean" property="chaineXML"/> </Promotion>

o

3. inversement, l’instruction jsp:getProperty name="monBean" property="chaineXML"/ revient à appeler la méthode getChaineXML() sur l’objet monBean.

o

o

n

n

n

7.2. ARCHITECTURES

229

C’est presque un document XML bien formé, à l’exception des instructions JSP pour les parties dynamiques. Il reste à intégrer la présentation des données obtenues avec un programme XSLT. JSP offre la possibilité 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" %> <% response.setContentType("text/plain"); %> <%@ page import="java.io.*,java.util.*,XMLBean" %> <jsp:useBean id="monBean" class="XMLBean"/> <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> <jsp:setProperty name="monBean" property="nomStation" value="Passac"/> <jsp:getProperty name="monBean" property="chaineXML"/> </Promotion> </xsltlib:apply>

Le programme JSP commence par déclarer l’utilisation de la librairie de balises xsl-1.0, avec le préfixe xsltlib. <%@ taglib uri="http://jakarta.apache.org/taglibs/xsl-1.0" prefix="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. Malheureusement la séparation n’est pas encore parfaite entre l’aspect « contenu » et l’aspect « présentation ».

o

n

On peut ensuite appliquer un programme XSLT avec la balise nom du fichier contenant le programme :

xsltlib:apply

en indiquant le

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.

7.3

XML dynamique

Les solutions conçues initialement pour HTML ne sont donc pas totalement adaptées à la création « dynamique » de XML. Les producteurs de logiciels ont donc proposé de nouveaux outils, en reprenant souvent en partie les principes des servlets ou des JSP. La présentation d’un panorama complet de ces outils dépasse le cadre de ce livre, d’autant qu’il s’agit d’un domaine qui évolue très rapidement. Nous présentons deux outils qui nous semblent assez représentatifs : 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 adaptation 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 transformations 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 document 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. Exemple 101 PromotionXSP.xml : Une document XSP
<?xml version="1.0" encoding="ISO-8859-1"?> <?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 (XSP) (autres transformations) Producteur (XSLT)

Document intermédiaire

Document intermédiaire

Formateur Réponse

Figure 7.7: Transformations successives dans Cocoon

<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
</Promotion> </xsp:page>

CHAPTER 7. PUBLICATION DE BASES DE DONNÉES

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" On trouve dans cette page les principaux éléments de XSP :

<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 resultat 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 prologue :
<?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.

q

p

Dans notre exemple l’élément évaluer une requête.

q

p

3.

xsp:expr 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). xsp:logic contient le code JDBC pour se connecter à la base et

q

p

2.

xsp:logic 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é &lt;) ;

q

p

q

p

1.

xsp:structure permet d’insérer des commandes d’inclusion de modules externes : dans le cas de Java on trouve des xsp:include qui donnent les packages à utiliser (l’équivalent de import) ;

7.3. XML DYNAMIQUE

233

Séparation des points de vue Pour l’instant on peut dire que nous n’avons pas gagné grand chose pour la séparation des points de vues puisqu’il a fallu insérer dans un document XML du code Java, avec du SQL et du JDBC. L’étape suivante est elle-aussi inspirée des JSP et consiste à définir des librairies de balises, strictement conformes à la syntaxe XML cette fois. Voici la nouvelle et dernière version de notre promotion : Exemple 102 PromotionXSPLib.xml : Un document XSP avec balise dynamique
<?xml version="1.0" encoding="ISO-8859-1"?> <?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 JavaTout 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 :

q

Ensuite l’élément racine xsp:page est supprimé. L’élément xsp:page 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 Promotion 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.

p

q

p

q

p

234

CHAPTER 7. PUBLICATION DE BASES DE DONNÉES

Exemple 103 PromotionXSP.xsl : La transformation XSLT associée à la balise dynamique
<?xml version="1.0" encoding="ISO-8859-1"?> <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> <xsp:page language="java" xmlns:xsp="http://www.apache.org/1999/XSP/Core"> <xsp:structure> <xsp:include>java.sql.*</xsp:include> </xsp:structure> <xsl:copy> <xsl:apply-templates/> </xsl:copy> </xsp:page> </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 match="@*|node()" priority="-1"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy>

7.3. XML DYNAMIQUE
</xsl:template> </xsl:stylesheet>

235

Puisque vous êtes maintenant familiarisé avec XSLT, vous pouvez constater que ce programme appliqué au document PromotionXSPLib.xml (page 233) va créer un document résultat identique à PromotionXSP.xml (page 230). Voici quand même quelques explications pour vous faciliter la tache :

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 gestionnaire de contenu peut se contenter de connaître la signification de ces balises, sans avoir à comprendre 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éveloppement 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

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.

r r r 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> les règles du programme XSLT recopient le document source : notez que la dernière a une priorité égale à -1 ; 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 ; 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 : ResultSet resultat = stmt.executeQuery ( "select * from Station " + "where nomStation=’<xsl:value-of select=’@nom’/>’");

XSQL

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. Exemples de documents XSQL Nous reprenons bien entendu notre base Station et notre document Promotion.xml (page 208) dans lequel nous souhaitons toujours intégrer dynamiquement des données issues de la base. Voici le document XSQL pour atteindre ce but : Exemple 104 Promotion.xsql : Le document XSQL avec la promotion pour Passac
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml-stylesheet href="Promotion.xsl" type="text/xsl"?> <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 xsql:query . 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 :

q

p

7.3. XML DYNAMIQUE

237

Ce document peut être traité dans plusieurs des architectures présentées précédemment : avec un programme 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 : Exemple 105 Station.xsql : Le document XSQL résultat de la requête sur Passac
<?xml version="1.0" encoding="ISO-8859-1"?> <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 ROWSET (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 ?xml-stylesheet 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 : Exemple 106 RequeteXSQL.xsql : Document XSQL avec paramètres
<?xml version="1.0" encoding="ISO-8859-1"?> <xsql:query connection="connexionVisiteur" xmlns:xsql="urn:oracle-xsql" rowset-element="{@racine}" row-element="{@element}"> SELECT * FROM Station WHERE nomStation=’{@nomStation}’ </xsql:query>

Les paramètres sont représentés par la syntaxe @nomParam. On peut les intégrer dans les attributs de xsql:query , ou dans la requête SQL elle-même. Cet exemple montre deux nouveaux attributs de cet élément : rowset-element définit le nom de l’élément racine du document produit, sa valeur par défaut étant ROWSET ;

row-element définit le nom de l’élément pour chaque ligne du résultat de la requête SQL.

q

q

p

p

q

r r r r p

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 ; la déclaration de l’espace de nom xmlns.

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. Options de sorties XML XSQL fournit des options à plusieurs niveaux pour les options de sorties XML. Tout d’abord l’élément xsql:query 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. Nom de l’attribut rowset-element row-element max-rows skip-rows id-attribute id-attribute-column null-indicator tag-case bind-params

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 :
SELECT nomStation, capacite, CURSOR(SELECT libelle, prix FROM Activite WHERE A.nomStation = S.nomStation) AS activites FROM Station S

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).

q

p

Description Nom de l’élément racine Nom de l’élément pour chaque ligne Nombre maximal de lignes ramenées par la requête Nombre de lignes à ignorer dans le résultat Nom de l’attribut XML identifiant une ligne (id) par défaut Nom de la colonne constituant la clé du résultat (par défaut XSQL crée un compteur de lignes) Si yes, inclut les valeurs à NULL avec un indicateur (par défaut une valeur à NULL n’engendre pas d’élément). Permet d’indiquer si les noms d’éléments sont en majuscules, minuscules, ou respectent la casse utilisée dans la requête. Définit une liste de paramètres

Table 7.1: Les attributs de l’élément xsql:query

7.4. PERSPECTIVES
<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>

239

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écessaires 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élicate, la conversion d’une représentation hiérarchique comme XML vers une représentation relationnelle « à plat » étant difficile à définir sans perte d’information. Ce qu’une base de données fait mieux que XML La question suivante se pose donc : puisque le modèle de XML semble plus puissant que celui des bases de données relationnelles, pourquoi ne pas utiliser ce modèle et s’épargner ainsi le processus de transformation des données ? Ou, pour dire les choses plus simplement, XML est-il un format de bases de données ? On peut être tenté de répondre « oui », en prenant comme définition minimale d’une base de données celle d’un ensemble d’information, structuré, et pouvant être sauvegardé sur disque. Cela étant XML ne propose pas beaucoup plus dans ce cadre qu’un simple fichier de texte. Même si on prend en compte le nombre considérable d’outils qui existent maintenant pour traiter des documents XML, de très grandes différences subsistent avec les services fournis par un système de gestion de base de données. Citons-en quelques-unes :

r r r

Typage : il est difficile d’exprimer des contraintes sur des données XML, les DTD étant très insuffisantes de ce point de vue. Or un SGBD est capable de prendre en compte un grand nombre de contraintes et de garantir leur satisfaction à tout moment. Interrogation : le langage qui se rapproche le plus de SQL est probablement ... XSLT ! Il n’offre cependant pas du tout les mêmes facilités, notamment en terme d’intégration avec les langages de programmation ; Mises à jour : même si on en parle moins, la possibilité d’effectuer des mises à jour (insertion, destruction, modifications) est essentielle. Il n’existe pas en XML d’équivalent aux clauses UPDATE, DELETE ou INSERT. On peut bien sûr utiliser un éditeur de texte, mais vec une absence à peu près totale de contrôle.

240

CHAPTER 7. PUBLICATION DE BASES DE DONNÉES

Certains systèmes utilisant XML comme format natif semblent en cours d’expérimentation, ce qui permettra(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 consé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 certaines 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.

Plus de typage : XMLSchema
TODO: qq paragraphes sur XMl Schema

Interrogation avec XML Query
TODO: idem avec XML Query

r r

Performances : les SGBD proposent des structures d’index très performantes pour accéder aux données. 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.

Chapter 8

Un serveur de publication XML
Sommaire

241

242

CHAPTER 8. UN SERVEUR DE PUBLICATION XML

Appendix A

L’environnement XML/Apache
Sommaire
A.1 Le projet XML/Apache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 A.2 Xalan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 A.2.1 Préliminaire : le Java Development Kit . . . . . . . . . . . . . . . . . . . . . . 245 A.2.2 Installation de Xalan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245 A.2.3 Test d’expressions XPath avec ApplyXPath . . . . . . . . . . . . . . . . . . . . 245 A.2.4 Effectuer des transformations XSLT avec Xalan . . . . . . . . . . . . . . . . . 246 A.2.5 Utiliser Xalan en Applet ou en Servlet . . . . . . . . . . . . . . . . . . . . . . 247 A.3 Cocoon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247 A.3.1 Tomcat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247 A.3.2 Cocoon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248 A.4 Intégration Apache/Tomcat/Cocoon . . . . . . . . . . . . . . . . . . . . . . . . . . . 249 A.4.1 Compilateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250 A.4.2 Apache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251 A.4.3 PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251 A.4.4 Lien Apache/Tomcat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251 A.4.5 Et Cocoon ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252

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 dont les objectifs sont plus généraux.

243

244

APPENDIX A. L’ENVIRONNEMENT XML/APACHE

A.1

Le projet XML/Apache

Le projet est subdivisé en plusieurs sous-projets. Voici ceux dont nous décrivons l’installation et l’utilisation. Deux sous-projets qui ne sont pas abordés ici sont Xang, un outils de production de pages web dynamiques en JavaScript, et SOAP, un protocole d’échanges de services XML. 1. Xerces, un ensemble d’outils d’analyse avec notamment des parseurs SAX et DOM ; 2. Xalan, un processeur XSLT ; 3. Cocoon, un l’environnement de publication web XML/XSLT ; 4. FOP, une librairie permettant de formatter en PDF des documents XSL-FO. 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 : 1. test interactif d’expressions XPath avec le programme Java ApplyXPath ; 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 ; 3. appel à l’API dans le cadre d’applications Java. Xalan s’installe et s’utilise extrêmement facilement et peut vous fournir en peu de temps un environnement 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 :

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 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.

r r r

pour le serveur web Apache, c’est www.apache.org ; pour Tomcat, c’est java.apache.org ; pour PHP, c’est www.php.net.

Xalan

A.2. XALAN

245

A.2.1

Préliminaire : le Java Development Kit

Toutes les applications sont écrites en Java. Il est donc indispensable de disposer d’un compilateur Java raisonnablement récent, par exemple le 1.2. Si Java n’est pas installé sur votre machine, récupérez la version la plus récente du Java Development Kit (JDK) sur le site http://java.sum.com et installez-la, par exemple dans /usr/local. Par exemple : 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.

A.2.2

Installation de Xalan

Récupérez la dernière version sur le site xml.apache.org. À l’heure où ces lignes sont écrites c’est XalanJava 2. Le fichier pour Linux s’appelle xalan-XXX.tar.gz, et pour Windows xalan-XXX.tar.zip (le XXX étant une partie variable en fonction des évolutions). Placez l’archive dans le répertoire de votre choix, et décompressez-la. cd /usr/local cp xalan-XXX.tar.gz . tar xvfz xalan-XXX.tar.gz Cela crée un répertoire xalan-XXX qui contient, entre autre : 1. le sous-répertoire bin avec toutes les archives (librairies) Java (.jar) ; 2. un répertoire docs avec toute la documentation au format HTML ; 3. un répertoire samples avec de nombreux exemples d’utilisation. 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 Windows. 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.

A.2.3

Test d’expressions XPath avec ApplyXPath

Xalan comprend un package org.apache.xpath.XPathAPI dédié au traitement des expressions XPath. On peut appeler les méthodes de ce package pour faire de la programmation XML/XPath, mais il y a beaucoup plus simple : un programme ApplyXPath permet d’interpréter interactivement des commandes XPath. Ce programme se trouve dans le sous-répertoire samples/ApplyXPath. Il faut commencer par compiler ApplyXPath.java :

246 javac ApplyXPath.java

APPENDIX A. L’ENVIRONNEMENT XML/APACHE

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 ExArbreXPath.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) : % java ApplyXPath ExArbreXPath.xml /A/B <output> <B att1="a1"> <D>Texte1</D> <D>Texte2</D> </B> <B att1="a2"> <D>Texte2</D> </B> </output> % java ApplyXPath ExArbreXPath.xml /A/*[@att2] <output> <C att2="a3" att3="15"/> </output> % java ApplyXPath ExArbreXPath.xml /A/C/@att3 <output> 15 </output> Noter que toutes les expressions XPath sont basées sur des chemins absolus.

A.2.4

Effectuer des transformations XSLT avec Xalan

Le processeur XSLT de Xalan peut, comme le processeur XPath, être appelé à partir de la ligne de commande. Voici la syntaxe de base : java java org.apache.xalan.xslt.Process -in fichierXML -xsl FichierXSL out fichierHTML L’option -out est facultative : par défaut le document résultat est affiché à l’écran. L’option -xsl est également facultative si le document XML contient une instruction de traitement ?xsl-stylesheet indiquant le programme XSLT à appliquer. Voici un exemple de transformation interactive, avec des documents extraits des exemples donnés dans le chapitre 1. % java org.apache.xalan.xslt.Process -in Alien.xml -xsl Film.xsl <html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Film: Alien</title> </head> <body bgcolor="white"> <p> <img height="220" align="left" SRC="Alien.gif"><h1> <i>Alien</i>
2 Le

fichier ApplyXPath.class doit être dans un répertoire référencés par la variable CLASSPATH.

q

p

A.3. COCOON
</h1>Science-fiction, <i>Etats Unis</i>, 1979</p> <p> Mis en sc&egrave;ne par <b>Ridley Scott</b> <h3>R&eacute;sum&eacute;</h3>Pr&egrave;s d’un vaisseau spatial &eacute;chou&eacute; sur une lointaine plan&egrave;te, des Terriens en mission d&eacute;couvrent de bien &eacute;tranges "oeufs". Ils en ram&egrave;nent un &agrave; bord, ignorant qu’ils viennent d’introduire parmi eux un huiti&egrave;me passager particuli&egrave;rement f&eacute;roce et meurtrier. </p> </body>

247

Ce mode d’utilisation de Xalan peut être utile pour tester des programmes XSLT, ainsi que pour engendrer 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 xsl:param name=’nomParam’ , la valeur valeurParam lui sera affectée. Nous renvoyons à la documentation de cet utilitaire (fichier docs/commandline.html) pour plus d’informations.

A.2.5

Utiliser Xalan en Applet ou en Servlet

Les exemples fournis par Xalan incluent une Applet qui fournit la possibilité d’effectuer des transformations côté client, et une servlet pour effectuer des transformations côté serveur. L’applet se trouve dans le répertoire samples/AppletXMLtoHTML, avec une petite documentation et un fichier appletXMLtoHTML.html qui permet d’avoir un aperçu de ses possibilités. En résumé, le principe consiste à charger les archives xerces.jar et xalan.jar dans le navigateur et à accéder aux fonctionnalités de transformation avec JavaScript ce qui permet de tout faire en local. L’inconvénient immédiatement rencontré est la nécessité de transférer du serveur au client les deux archives qui sont très volumineuses : 1,8M pour xerces.jar et 800K pour xerces.jar !! En ce qui conerne la servlet, elle se trouve dans samples/servlet (fichier ApplyXSLT.java avec un peu de documentation. La mise en place d’une servlet est un peu compliquée par la nécessité d’utiliser un servlet container comme Tomcat. De plus Cocoon que nous présentons dans la prochaine section offre des fonctionnalités plus puissantes.

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.

q

p

248

APPENDIX A. L’ENVIRONNEMENT XML/APACHE

Tomcat est un sous-projet de Jakarta/Apache, le projet de la fondation Apache dédié aux environnements 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 COCOON_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. <Context path="/cocoon" docBase="webapps/cocoon" debug="0" reloadable="true" > </Context>

A.4. INTÉGRATION APACHE/TOMCAT/COCOON

249

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. 3. Finalement, il reste à placer des fichiers XML dans TOMCAT_HOME/webapps/cocoon. Commencez par créer les répertoires : mkdir TOMCAT_HOME/webapps/cocoon mkdir TOMCAT_HOME/webapps/cocoon/WEB-INF Puis copiez les fichiers fournis par Cocoon : 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> 4. Dernière opération : copiez les fichiers de COCOON_HOME/samples dans TOMCAT_HOME/webapps/cocoon/samples. Voilà ! Relancez Tomcat, et essayez d’accéder à l’URL http:localhost:8080/cocoon/Cocoon.xml ou http:localhost:8080/cocoon/samples. En principe tout doit bien se passer mais... il se peut que le problème suivant survienne : Tomcat initialise automatiquement la variable CLASSPATH au démarrage, en prenant tous les .jar de TOMCAT_HOME/lib. Or, pour que Cocoon fonctionne, le .jar de Xerces doit être placé avant les autres .jar dans le CLASSPATH. Si Cocoon refuse de traiter un document XML, c’est probablement de là que vient le problème (un jour où l’autre cette anomalie sera corrigée). La manipulation suivante devrait tout régler : renommez les fichiers lib/xml.jar et lib/parser.jar en lib/zxml.jar et lib/zparser.jar. C’est scabreux, mais efficace. Quand tout marche, vous êtes équipés d’un environnement de publication web à base de XML. Nous vous invitons à consulter les exemples fournis dans cocoon/samples, en accédant avec votre navigateur à l’URL 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.

A.4

Intégration Apache/Tomcat/Cocoon

Tomcat est un serveur web généraliste qui est capable de fournir des documents HTML, de gérer des servlets et de traiter des Java Server Pages, une forme simplifiée des servlets permettant d’intégrer souplement balises HTML et instructions Java. En pratique il n’est cependant pas recommandé de baser en

250

APPENDIX A. L’ENVIRONNEMENT XML/APACHE

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 Apache Transfert requêtes Servlets Cocoon Programmes CGI Scripts PHP Pages HTML Doc. XML Pages JSP 8080 Tomcat Servlet A

Figure A.1: Architecture Apache-Tomcat-Cocoon Toutes les requêtes peuvent être adressées au port 80 : s’il s’agit de demandes HTTP pour des documents 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 exemple à 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 : ./configure --prefix=/usr/local/apache --enable-module=so make make install 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 La dernière commande copie le fichier libphp4.so dans /usr/local/apache/libexec, et indique à Apache que le module doit être chargé en plaçant la commande suivante dans le fichier httpd.conf : LoadModule php4_module libexec/libphp4.so

Il ne reste plus qu’à décommenter les lignes suivantes dans httpd.conf pour que PHP soit disponible : AddType application/x-httpd-php .php AddType application/x-httpd-php-source .phps 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.4

Lien Apache/Tomcat

Il reste à bien répartir le travail entre Apache et Tomcat. Le premier doit prendre en charge tous les documents classiques, type HTML, PHP ou autre, et passer la main au second pour les servlets, les JSP ou les documents XML. Initialement, on utilisait le module JServ pour permettre à Apache de prendre en compte les servlets. Maintenant il est plus simple d’utiliser le module JK. Un fichier mod_jk.so est disponible sur java.apache.org avec Tomcat. Installez ce fichier dans /usr/local/apache/libexec. Si par malheur ce fichier ne correspond pas à votre distribution, il n’y a plus qu’à le recompiler...

252

APPENDIX A. L’ENVIRONNEMENT XML/APACHE

1. Récupérez le code source de Tomcat sur java.apache.org 2. Décompressez-le, et allez dans src/native/apache1.3 (vérifiez la version d’Apache). 3. La commande de compilation est la suivante : apxs -o mod_jk.so -I../jk -I/usr/local/jdk/include \ -I/usr/local/jdk/include/linux -I../jk \ -c *.c ../jk/*.c Le programme apxs est dans /usr/local/apache/bin, et il faut vérifier que le JDK est bien installé dans /usr/local, ou modifier les options -I de manière appropriée. 4. Copiez le fichier mod_jk.so dans /usr/local/apache/libexec. Maintenant il faut inclure dans le fichier dans httpd.conf les directives pour charger le module JK et pour lui signaler que certaines URL doivent être transmises à Tomcat. Par chance (!) Tomcat produit automatiquement un fichier de configuration qui doit suffire dans la plupart des cas. Ce fichier est dans TOMCAT_HOME/conf et s’appelle mod_jk.conf-auto. Attention : ce fichier est regénéré à chaque lancement de Tomcat. Commencez par le copier avant de faire une modification : cp mod_jk.conf-auto mon-mod-jk.conf Il faut inclure ce fichier dans le httpd.conf : Include /usr/local/jakarta-tomcat-3.2.1/conf/mon-mod-jk.conf Ce fichier inclut lui-même un autre fichier, nommé workers.properties, qui indique à Apache comment communiquer avec Tomcat. Il faut éditer workers.properties et mettre les bonnes valeurs. Par exemple : workers.tomcat_home=usr/local/jakarta-tomcat-3.2.1 workers.java_home=/usr/lib/jdk1.1.8 ps=/ Bien. Redémarrons Apache et Tomcat, et essayons d’accéder à l’URL suivante : http://localhost/examples/jsp/ C’est le serveur Apache qui a reçu la requête (noter l’absence du port 8080). Comme elle commence par /examples, Apache transmet cette requête à Tomcat. Cette redirection est spécifiée par une directive JkMount dans le fichier mon-mod-jk.conf.

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 <Location /cocoon/WEB-INF/ > AllowOverride None deny from all </Location> Maintenant, vous devriez, en accédant à Apache, pouvoir rediriger vers Cocoon une URL comme : localhost://cocoon/Cocoon.xml

Appendix B

Référence XPath/XSLT
Sommaire
B.1 Éléments XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 B.2 Fonctions XPath/XSLT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282

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.

B.1

Éléments XSLT

La liste des éléments XSLT a été donnée dans le chapitre 3, avec une répartition en éléments de premier niveau (tableau 3.1 page 112) et en instructions (tableau 3.2 page 114). Dans ce qui suit nous reprenons, dans l’ordre alphabétique cette fois, tous ces éléments en donnant la syntaxe complète, une description de la sémantique, et éventuellement un ou deux exemples complétant ceux que vous avez déjà pu trouver dans les chapitres qui précèdent. Chaque élément est défini par son type, la liste de ses attributs, et le type de son contenu. Pour les attributs nous distinguons ceux qui sont obligatoires, indiqués en gras, de ceux qui sont optionnels, indiqués simplement avec une police à chasse fixe. Il est possible d’exprimer des contraintes sur le type d’un attribut ou d’un élément sous la forme de catégories syntaxiques qui définissent la forme que peut prendre la valeur de l’attribut ou le contenu de l’élément. Quand c’est le cas nous l’indiquons dans la description de l’élément. Les catégories syntaxiques les plus courantes sont rappelées dans le tableau B.1. Nous utilisons les conventions des DTD pour indiquer éventuellement l’ordre ou la multiplicité d’apparition de telle ou telle catégorie syntaxique dans le contenu d’un élément. xsl:apply-imports Syntaxe

q
253

xsl:apply-imports/

p

254 Désignation Expression QName

APPENDIX B. RÉFÉRENCE XPATH/XSLT
Description Toute expression XPath 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), correspondant à un espace de nom déclaré, et d’un nom local, obligatoire (ici monEl) 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. Les patterns appartiennent au sous-ensemble des expressions XPath autorisées dans l’attribut match de l’élément xsl:template : voir page 119 Un Uniform Resource Identifier. Il prend le plus souvent la forme d’un fichier local, ou d’une adresse HTTP Un corps de règle (voir page 111) est toute combinaison 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

NCName

Pattern

URI

Corps de règle

Table B.1: Catégories syntaxiques

Description Quand on importe un programme XSLT dans un autre, les règles du programme importé ont une priorité infé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:applyimports. Cette fonctionnalité s’apparente au mécanisme de surcharge de méthode dans la programmation orientéeobjet. 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 PERSONNE (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/>

q

p

B.1. ÉLÉMENTS XSLT
</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>

255

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. Exemple 108 JohnDoe.xml : Le salarié John Doe
<?xml version="1.0" encoding="ISO-8859-1"?> <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 Exemple 109 ApplyImports.xsl : Utilisation de xsl:apply-imports
<?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="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/> <b> Poste</b> : <xsl:value-of select="POSTE"/>, <b> Salaire </b> :<xsl:value-of select="SALAIRE"/> </xsl:template> </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 xsl:apply-templates Syntaxe xsl:apply-templates select=Expression mode=QName ( xsl:with-param /xsl:apply-templates

APPENDIX B. RÉFÉRENCE XPATH/XSLT

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électionné la règle à appliquer. Les deux attributs possibles sont optionnels. L’attribut select est une expression 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:applytemplates 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 xsl:atrribute name=QName namespace=URI Corps de règle

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.

q

/xsl:attribute

s

q

p

q

p

q

q

q

p

p p p

|

xsl-sort

)

B.1. ÉLÉMENTS XSLT
Exemples

257

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. Exemple 110 Attribute1.xsl : L’élément xsl:attributes
<?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="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>

Le résultat de la transformation est : ?xml version="1.0" encoding="UTF-8"? PERSONNE NOM="Doe" PRENOM="John"/ Maintenant on obtient exactement le même résultat avec le programme suivant :

Exemple 111 Attribute2.xsl : Un programme équivalent, sans xsl:attributes
<?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="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 PERSONNE 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. Exemple 112 Attribute3.xsl : L’élément xsl:attributes, avec une valeur calculée
<?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="PERSONNE"> <PERSONNE NOM="{NOM}" PRENOM="{PRENOM}"> <xsl:attribute name="AGE"> <xsl:value-of select="2002 - substring(NAISSANCE/text(),7)"/>

q

p

q

q

p

p

258
</xsl:attribute> </PERSONNE> </xsl:template> </xsl:stylesheet>

APPENDIX B. RÉFÉRENCE XPATH/XSLT

On pourrait certes mettre l’expression directement dans la balise PERSONNE , 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 xsl:attribute-set name=QName use-attribute-sets=QNames xsl:attribute /xsl:attribute-set

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, useattribute-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:useattribute-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 body . Exemple 113 AttributeSet.xsl : L’élément xsl:attribute-set
<?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <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>

q

p

q

p

q

s tq

q

p

p p

B.1. ÉLÉMENTS XSLT

259

<body xsl:use-attribute-sets="MonStyle"> Exemple de use-attribut-sets </body> </html> </xsl:template>

</xsl:stylesheet>

L’application de ce programme produit le résultat suivant : Exemple 114 AttributeSet.html : Résultat du programme précédent
<html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Ficher personne</title> </head> <body bgcolor="white" font-name="Helvetica" font-size="18pt"> Exemple de use-attribut-sets </body> </html>

xsl:call-template Syntaxe xsl:call-template name=QName xsl:with-param /xsl:call-template

Description L’élément xsl:call-template permet d’appeler une règle par son nom. Il est utilse dès qu’un fragment 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 programmation 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

q

xsl:choose

s tq

q

q

p

p p p

260 xsl:when xsl:otherwise /xsl:choose

APPENDIX B. RÉFÉRENCE XPATH/XSLT

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. Exemple 115 RefChoose.xsl : Exemple d’utilisation de xsl:choose
<?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"> <CHAPITRES> <xsl:apply-templates select="CHAPITRE"/> </CHAPITRES> </xsl:template> <xsI:template match="CHAPITRE"> <xsl:choose> <xsl:when test="@LONGUEUR &lt; 20"> Le chapitre "<xsl:value-of select="TITRE"/>" est court </xsl:when> <xsl:when test="@LONGUEUR &lt; 30"> Le chapitre "<xsl:value-of select="TITRE"/>" est moyen </xsl:when> <xsl:when test="@LONGUEUR &gt;= 30"> Le chapitre "<xsl:value-of select="TITRE"/>" est long </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 « » ne peut être utilisé directement dans le test et doit être remplacé par une référence à l’entité prédéfinie &lt;.

x

w vq

u vq

q

p p

p

B.1. ÉLÉMENTS XSLT
Voir également page 134.

261

xsl:comment Syntaxe xsl:comment corps de règle /xsl:comment

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 !- ... - 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 contenu 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 : !-- instanciation du corps de règle -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. Exemple 116 XBook.xml : Une description du livre
<?xml version="1.0" encoding="ISO-8859-1"?> <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>

y

y

x

y

y

x

x x

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 commentaire 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

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>

y

xsl:copy use-attribute-sets=QNames corps de règle /xsl:copy

y

x x

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>

Le résultat obtenu est : Exemple 120 ResultCopy.xml : Résultat du programme précédent
<?xml version="1.0" encoding="ISO-8859-1"?> Voici un élément : "<A/>" <attribut att1="2"/> Voici du texte : " " Voici un commentaire : "<!-- Test de xsl:copy -->" Voici du texte : " " Voici un élément : "<B/>" Voici du texte : "Contenu B" Voici du texte : " " Voici un élément : "<C/>" Voici du texte : "Contenu C" Voici du texte : " "

Ce résultat appelle quelques explications :

(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 apparaissent ici ;

y

x

1. les attributs sont copiés dans l’élément courant : ici on a introduit un élément littéral dans la règle qui traite les attributs, ce qui implique

attribut

264

APPENDIX B. RÉFÉRENCE XPATH/XSLT

3. enfin les éléments apparaissent vides puisqu’ils n’ont pas de valeur. 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. Exemple 121 Copy2.xsl : Copie avec exclusion des commentaires
<?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="*|@*|text()"/> </xsl:template> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="*|@*|text()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>

La première règle s’applique à la racine du document et déclenche un appel xsl:apply-templates en sélectionnant tous les types de nœuds à l’exception des commentaires (et des instructions de traitement). Pour tous les nœuds sélectionnés (y compris les attributs) la seconde règle s’applique : elle effectue une copie du nœud courant, et s’appelle récursivement. Il est important de remarquer que si les commentaires sont exclus, c’est parce qu’il n’existe pas de règle par défaut pour ce type de nœud. En revanche, si on souhaite exclure les nœuds de type Text, il faut indiquer explicitement une règle qui ne fait rien, comme dans le programme ci-dessous : Exemple 122 Copy3.xsl : Copie avec exclusion des nœuds texte
<?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="@*|node()"> <xsl:copy> <xsl:apply-templates select="*|@*|comment()"/> </xsl:copy> </xsl:template> <xsl:template match="text()"/> </xsl:stylesheet>

B.1. ÉLÉMENTS XSLT
xsl:copy-of Syntaxe

265

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. Exemple 123 CopyOf.xsl : Copie en profondeur
<?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/> </xsl:template> <xsl:template match="A"> <xsl:copy/> <xsl:copy-of select="B"/> </xsl:template> </xsl:stylesheet>

Appliqué au document Copy.xml, on obtient le résultat suivant : Exemple 124 ResultCopyOf.xml : Résultat du programme CopyOf.xsl
<?xml version="1.0" encoding="ISO-8859-1"?> <A/><B>Contenu B</B>

xsl:decimal-format Syntaxe xsl:decimal-format name=nom decimal-separator=séparateurDécimale grouping-separator=séparateurGroupe infinity=infini minus-sign=signeMoins NaN=symboleNaN percent=symbolePourCent

y

xsl:copy select=Expression /xsl:copy-of

y

x x x

266

APPENDIX B. RÉFÉRENCE XPATH/XSLT

Description blabla

xsl:document Syntaxe 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 /xsl:document

Description XSLT 1.1

xsl:element Syntaxe xsl:element name={QName} namespace={URI} use-attribute-sets=QNames corps de règle /xsl:element

Description Cette instruction permet d’insérer un élément dans le document résultat. Son principal intérêt est de permettre 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:attributeset : voir page 259.

y

per-mille=symbolePourMille zero-digit=complémentZéro digit=complémentDigit pattern-separator=séparateurPlusMoins /xsl:decimal-format

y

y

y

y

y

x x x x x

B.1. ÉLÉMENTS XSLT
Exemples

267

Le programme suivant transforme l’attribut ISBN du document XBook.xml en élément, et copie en profondeur les éléments TITRE et EDITEUR . Exemple 125 Element.xsl : Exemple d’utilisation de xsl:element
<?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="/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>

xsl:fallback Syntaxe xsl:fallback corps de règle /xsl:fallback

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. xbook:refChapitre no=’1’ fallback Instruction inconnue /fallback /xbook:refChapitre

xsl:for-each Syntaxe

y

xsl:for-each select=Expression

y

x

Évidemment on pourrait tout aussi bien inclure

y

y

x

y

y

y

x

ISBN

comme un élément littéral.

y

y

y

x x x x x x x

268 ( xsl:sort , corps de règle) corps de règle /xsl:for-each

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:foreach 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 xsl:if test=Expression corps de règle /xsl:if

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

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.

y

xsl:import href=URI/

z {y y

y

x

y

x x x x

B.1. ÉLÉMENTS XSLT
xsl:include Syntaxe

269

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 xsl:key name=QName match=pattern use=expression/

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. Exemple 126 Films.xml : Quelques films
<?xml version="1.0" encoding="ISO-8859-1"?> <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">

y

xsl:include href=URI/

y

x x

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 FILM . Exemple 127 Key.xsl : Utilisation de xsl:key
<?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="FilmsDate" match="FILM" use="@ANNEE"/> <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

Description Cette instruction permet d’afficher un message sur la « console » du processeur XSLT. Elle peut principalement ê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 xsl:namespace-alias stylesheet-prefix=préfixe | "#default" result-prefix=préfixe | "#default" /

y

y

xsl:message terminate=("yes" | "no") corps de règle /xsl:message

y

x

y

x x x

B.1. ÉLÉMENTS XSLT
Description

271

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 stylesheetprefix, 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. Exemple 129 NamespaceAlias2.xsl : Utilisation de xsl:NamespaceAlias
<?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <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 xsl:number

x

272

APPENDIX B. RÉFÉRENCE XPATH/XSLT
level="single" | "multiple" | "any" count=pattern from=pattern value=expression format=chaîneFormat lang=codeLangage letter-value="alphabetic" | "traditional" grouping-separator=séparateurGroupe grouping-size=tailleGroupe /

Description Blabla

xsl:otherwise Syntaxe xsl:otherwise corps de règle /xsl:otherwise

Description Cet élément est subordonné à l’élément xsl:choose : voir page 261.

xsl:output Syntaxe 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 /

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.

y

y

y

y

x x x

B.1. ÉLÉMENTS XSLT

273

L’attribut omit-xml-declaration (avec pour valeur par défaut no) peut être utilisé pour supprimer 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 xsl:param name=QName select=Expression corps de règle /xsl:param

Description Cet élément peut être utilisé de deux manières : 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 Voir la section consacrée au passage de paramètres, page 132.

xsl:preserve-space Syntaxe xsl:preserve-space elements=liste-éléments/

y

y

y

x x x

274 Description

APPENDIX B. RÉFÉRENCE XPATH/XSLT

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 : xsl:preserve-space elements="*"/

Exemples L’exemple suivant est un document avec l’indentation habituelle pour clarifier la hiérarchie des éléments. A et B ont tous deux deux fils de type Text vides, encadrant un élément. Exemple 130 Space.xml : Un document XML avec indentation
<?xml version="1.0" encoding="ISO-8859-1"?> <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. Exemple 131 PreserveSpace.xsl : Exemple de xsl:preserve-space et xsl:strip-space
<?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <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 A et 3 pour B .

y x

y x

y

y x

y x

x

B.1. ÉLÉMENTS XSLT
xsl:processing-instruction Syntaxe xsl:processing-intruction name=QName corps de règle /xsl:processing-instruction

275

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 xsl:sort select=expression lang=codeLangue order=("ascending" | "descending") case=("lower-first" | "upper-first") data-type= ("text" | "number" ) /

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 premiè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. Exemple 132 RefSort.xsl : Exemple de xsl:sort

y

y

y

x x x

276

APPENDIX B. RÉFÉRENCE XPATH/XSLT

<?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="/"> <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 informartions sur les metteurs en scènes. Exemple 133 Artistes.xml : Les metteurs en scène
<?xml version="1.0" encoding="ISO-8859-1"?> <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. Exemple 134 RefSort2.xsl : Films triés sur l’année de naissance du réalisateur
<?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: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 ARTISTE dont l’élément fils NOM 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. 1. le nœud courant est un des élément de type FILM du document Films.xml ; 2. le nœud contexte dépend des étapes de l’expression XPath : dans l’expression ci-dessus, si on remplaçait current par « . », on désignerait un élément ARTISTE du document Artistes.xml, et pas un film. La fonction current() désigne toujours l’élément courant (voir page 283). xsl:strip-space Syntaxe xsl:strip-space

y

y

x

x

y

x

x

278

APPENDIX B. RÉFÉRENCE XPATH/XSLT

Description Cet élément donne la liste des éléments du documents source dont les fils de type Text constitués uniquement 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 xsl:stylesheet id=idElément extension-elements-prefixes=tokens exclude-result-prefixes=tokens version=number éléments de premier niveau /xsl:stylesheet

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 recommandation 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 xsl:template name=QName match=Pattern mode=QName priority=number

y

elements=QNames/

y

y

y

x x x

B.1. ÉLÉMENTS XSLT

279 , corps de règle)

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:applytemplates. L’attribut mode permet de définir plusieurs catégories de règles à appliquer dans des contextes différents. Exemple Voir la section du chapitre 3 consacrée aux règles, page 119. xsl:text Syntaxe

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 &lt; pour ou &amp; 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 document 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. Exemple 135 Text.xsl : Exemple de xsl:text
<?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:text disable-output-escaping="yes"> Si 0 &lt; 1, alors 1 &gt; 0, &amp; vice et versa

y

xsl:text disable-output-escaping=("yes" | "no") #PCDATA /xsl:text

z {y x

( xsl:param /xsl:template

y

y

x

x x x

280
</xsl:text> </xsl:template> </xsl:stylesheet>

APPENDIX B. RÉFÉRENCE XPATH/XSLT

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é. 1, alors 1 0, Si 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 xsl:value-of select=Expression disable-output-escaping=("yes" | "no") /

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 xsl:variable name=QName select=Expression / corps de règle /xsl:variable

y

y

y

y

x

x x x

B.1. ÉLÉMENTS XSLT
Description

281

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 importé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 xsl:value-of test=Expression / corps de règle /xsl:variable

Description Cet élément est subordonné à l’élément xsl:choose : voir page 261.

xsl:with-param Syntaxe xsl:value-of name=QName select=Expression / corps de règle /xsl:with-param

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.

y

y

y

y

x x x x

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 programmation 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.

B.2

Fonctions XPath/XSLT

Les fonctions qui suivent sont utilisables dans toute expression XPath apparaissant dans un programme XSLT. La plupart sont des fonctions définies dans la recommandation XPath, mais certaines (formatnumber(), document(), key() par exemple) sont spécifiques à XSLT. Nous ne les distinguons pas dans ce qui suit. Une fonction prend zéro, un ou plusieurs arguments, parfois optionnels. Nous utilisons les conventions habituelles des DTD : arg indique un argument qui peut être répété 0 ou plusieurs fois, arg? indique un argument optionnel, etc. En général le type de ces arguments est l’un des quatre types de base XPath (number, string, boolean et node-set), ou l’union de ces quatres types que nous dénotons objet comme dans la recommandation XPath. Bien entendu un argument peut lui-même être le résultat d’une autre fonction, ou bien être une expression XPath dont l’évaluation donnera la valeur à passer à la fonction. Si le type de cette valeur ne correspond pas au type attendu, par exemple number, le processeur tentera d’effectuer une conversion au moment de l’appel de la fonction. boolean Boolean boolean (object arg) La fonction boolean() convertit son argument arg en booléen. Les règles de conversion ont été présentées dans le chapitre 2, page 103. Rappelons-les briévement : 1. un numérique à zéro est converti à false, toutes les autres valeurs sont true ; 2. une chaîne de longueur nulle est false, toutes les autres sont true ; 3. un node-set vide est false, sinon la conversion renvoie true.

ceiling number ceiling (number valeur) Cette fonction renvoie le plus petit entier immédiatement supérieur à valeur. Par exemple ceiling(1.5) est 2. concat string concat (string chaîne1, string chaîne2, string chaines ) Cette fonction concatène les chaînes de caractères (au moins deux) qui lui sont passées en argument.

z

z

B.2. FONCTIONS XPATH/XSLT
contains Boolean contains (string chaîne, string sous-chaîne) Cette fonction teste si chaîne contient sous-chaîne, et renvoie true ou false selon le cas. count Boolean count (node-set nœuds) Cette fonction compte le nombre de nœuds dans le node-set passé en argument. current node-set current ( )

283

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 prendre 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. xsl:apply-templates match="../*[.=current()]" La réféence de l’élément xsl:sort présente un exemple où l’utilisation de current est impérative.

document node-set document (node-set uri, node-seturi) TO DO element-available Boolean element-available (string nomElément) 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.

y

x

284 false Boolean false ()

APPENDIX B. RÉFÉRENCE XPATH/XSLT

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 équivalente. floor number floor (number valeur) 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 Boolean function-available (string nomFonction) 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 string generate-id (node-set nœud) TO DO id node-set id (object arg) TO DO key node-set key (string nomClé, object expression) 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
Exemples Voir page 270. lang Boolean lang (string codeLangue)

285

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électionné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 string local-name (node-set nœud?) 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 string name (node-set nœud?) La fonction renvoie le nom complet, comprenant éventuellment 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. namespace-uri string namespace-uri (node-set nœud?) La fonction renvoie l’URI associée au préfixe du nœud passé en argument. normalize-space string normalize-space (string chaîne?) TO DO

286 not Boolean not (Boolean valeur?) Cette fonction calcule la négation de son argument. number number number (object valeur?) Cette fonction convertit l’argument valeur en numérique. position number position ()

APPENDIX B. RÉFÉRENCE XPATH/XSLT

Cette fonction renvoie la position de l’objet courant dans l’ensemble de nœud que constitue le contexte XPath. round number round (number valeur) Cette fonction arrondit son argument à l’entier le plus proche. starts-with Boolean starts-with (string chaîne, string sous-chaîne) Cette fonction teste si chaîne débute avec sous-chaîne. string string string (object valeur?) TO DO string-length number string-length (string chaîne?) TO DO substring string substring (string chaîne, number début, number longueur?) TO DO

B.2. FONCTIONS XPATH/XSLT
substring-after string substring-after (string chaîne, string sous-chaîne) TO DO substring-before string substring-before (string chaîne, string sous-chaîne) TO DO sum number sum (node-set nœuds) Cette fonction effectue la somme des valeurs des nœuds dans le node-set passé en argument. system-property number system-property (string nomParamètre) TO DO translate string translate (string chaîne, string arg1, string arg2) TO DO true Boolean true ()

287

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 string unparsed-entity-uri (string nom) TO DO

You're Reading a Free Preview

Télécharger
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->