Travailler avec XSLT

Gazette Linux n°89 — Avril 2003

Daniel Guerrero

Article paru dans le n°89 de la Gazette Linux d'avril 2003.

Traduction française par Guillaume Lelarge .

Relecture de la traduction française par Joëlle Cornavin .

Article publié sous Open Publication License. La Linux Gazette n'est ni produite, ni sponsorisée, ni avalisée par notre hébergeur principal, SSC, Inc.


Table des matières
1. Rencontre avec XSLT
1.1. Sélection des attributs
1.2. Variables
1.3. Tri
1.4. Instruction if
1.5. Instruction for-each
1.6. Instruction choose
2. Processeurs XSLT
2.1. Saxon
2.2. xsltproc
3. Références

Le eXtensible Stylesheet Language Transformations (XSLT est utilisé principalement pour transformer les données XML en données HTML, mais avec XSLT, nous pourrions effectuer la transformation depuis le XML (ou quelque chose qui utilise les espaces de noms XML, comme le RDF) en quoi que ce soit dont nous avons besoin , depuis le XML vers du texte).

Le w3 établit que XSL (eXtensible Stylesheet Language) se compose de trois parties :


1. Rencontre avec XSLT

Tout d'abord, nous devons spécifier que notre document XML sera une feuille de style XSL et importer l'espace de noms XML :


<xsl:stylesheet version="1.0"
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

...

</xsl:stylesheet>

Après cela, l'élément principal que nous utiliserons sera le xsl:template match, qui est appelé lorsque le nom d'un nœud XML correspond à la valeur de xsl:template match :


<xsl:stylesheet version="1.0"
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/"> <!-- '/' is taken from XPath and will match with the root element -->
    <!-- do something with the attributes  of the node -->
</xsl:template>

Dans le xsl:template match, nous pourrions obtenir un attribut du nœud avec l'élément xsl:value-of select ainsi que le nom de l'attribut. Créons d'abord un fichier XML d'exemple avec quelques informations :


<!-- hello.xml -->

<hello>
   <text>Hello World!</text>
</hello>

Et voici le fichier XSLT qui extraiera le text de l'élément racine (hello) :


<!-- hello.xsl -->
<xsl:stylesheet version="1.0"
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/"> 
  <html> 
    <head>
      <title>Extracting <xsl:value-of select="//text"/> </title>
       <!--  in this case '//text' is: 'hello/text' but because I'm a lazy person... I will short it with XPath  -->
    </head>

    <body>
       <p>
           The <b>text</b> of the root element is: <b><sl:value-of select="//text"/></b>
       </p> 
    </body>
  </html>
</xsl:template>

</xsl:stylesheet>

Voici la sortie HTML :

  
<!-- hello.html -->
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

      <title>Extracting Hello World! </title>
   </head>
   <body>
      <p>
         The <b>text</b> of the root element is: <b>Hello World!</b>
      </p>
   </body>
</html>

1.1. Sélection des attributs

@att correspondra à l'attribut att. Par exemple :


<!-- hello_style.xml -->

<hello>
<text color="red">Hello World!</text>
</hello>

Voici le XSLT :


<!-- hello_style.xsl -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
<html>
<head>
<title>Extracting <xsl:value-of select="//text"/> </title>
</head>

<body>
<p>
The <b>text</b> of the root element is: <b><xsl:value-of select="//text"/></b>
and his <b>color</b> attribute is: <xsl:value-of select="//text/@color"/>


</body>
</html>
</xsl:template>

</xsl:stylesheet>

Voici ce que sera la sortie HTML :


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<title>Extracting Hello World! </title>
</head>
<body>
<p>
The <b>text</b> of the root element is: <b>Hello World!</b>
and his <b>color</b> attribute is: red


</body>
</html>

Si vous pensez utiliser cette information, mettez dans ce cas le texte (Hello World!) en rouge. Il y a deux façons possibles pour ce faire : créer des variables et les utiliser dans les attributs de la police, par exemple ou utiliser l'élément xsl:attribute.


1.2. Variables

Les variables pourraient être utilisées pour contenir des constantes ou la valeur d'un élément.

Affecter des constantes est simple :


<!-- variables.xsl -->

<xsl:stylesheet version="1.0"
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">


<xsl:template match="/"> 

<!--  definition of the variable  -->
<xsl:variable name="path">http://somedomain/tmp/xslt</xsl:variable>

  <html>
    <head>
      <title>Examples of Variables</title>
  </head>

    <body>
       <p>
           <a href="{$path}/photo.jpg">Photo of my latest travel</a>
       </p> 
    </body>
  </html>
</xsl:template>

</xsl:stylesheet>
</screen>

Voici la sortie HTML :


<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

      <title>Examples of Variables</title>
   </head>
   <body>
      <p><a href="http://somedomain/xslt/photo.jpg">Photo of my latest travel</a></p>
   </body>
</html>

Vous pouvez aussi obtenir la valeur de la variable en la sélectionnant à partir des valeurs ou des attributs des nœuds :


<!-- variables_select.xsl -->

<xsl:stylesheet version="1.0"
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">


<xsl:template match="/"> 
    <html>
       <head>
         <title>Examples of Variables</title>>
       </head>
       <body>
           <xsl:apply-templates select="//photo"/>
       </body>
    </html>
</xsl:template>

<xsl:template match="photo">
    <!--  definition of the variables  -->
    <xsl:variable name="path">http://somedomain/tmp/xslt</xsl:variable> >
    <xsl:variable name="photo" select="file"/> 
     <p> 
       <a href="{$path}/{$photo}"><xsl:value-of select="description"/></a>
     </p>
</xsl:template>

</xsl:stylesheet>

Et le source XML :


<!-- variables_select.xml -->

<album>
   <photo>
      <file>mountains.jpg</file>
      <description>me at the mountains</description>
   </photo>

   <photo>>
      <file>congress.jpg<file>
      <description>me at the congress</description>
   </photo>

    <photo>
      <file>school.jpg</file>
      <description>me at the school</description>
   </photo>
</album>

La sortie HTML :


<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">   
      <title>Examples of Variables</title>
   </head>
   <body>
      <p><a href="http://somedomain/tmp/xslt/mountains.jpg">me at the mountains</a><</p>
      <p><a href="http://somedomain/tmp/xslt/congress.jpg">me at the congress</a></p>>
     <p><a href="http://somedomain/tmp/xslt/school.jpg">me at the school</a></p>
   </body>
</html>

Peut-être aurez-vous noté que la correspondance de l'élement photo est appelée trois fois du fait que xsl:apply-templates, chaque fois XSLT trouve un élément qui lui correspond, est appelé le xsl:template match qui lui correspond.

Donc, vous êtes impatient d'essayer d'écrire le texte en rouge ? Essayez de le faire avec des variables. Si vous n'y arrivez pas, ouvrez la page outils/lg89-A/hello_style_variables.xsl.


1.3. Tri

XSLT peut trier le traitement des balises XML avec <xsl:sort select="sort_by_this_attribute>, cet élément devant être placé dans un élément xsl:apply-templates. Le tri peut s'effectuer par rapport à un élément ou à un attribut XSLT, par ordre ascendant ou descendant. Vous pourriez également spécifier l'ordre de la casse (si un caractère en minuscule doit être placé avant un caractère en majuscule, ou vice versa).

J'utiliserai l'exemple de l'album, et j'y ajouterai seulement l'élément de tri :


<xsl:apply-templates select="//photo">
<xsl:sort select="file" order="descending">
</xsl:apply-templates>

Ceci ne modifiera que l'ordre des photos dans le fichier HTML. En fait, XSLT triera d'abord tous les éléments photos de notre fichier XML et les enverra à l'élément template-match dans cet ordre. C'est pourquoi il faut que xsl:sort soit à l'intérieur de xsl:apply-templates.

Les fichiers XSL et HTML sont dans les exemples. Vous pouvez les obtenir sur les liens suivants :


1.4. Instruction if

Il y aura des cas où vous devrez insérer du texte si un élément (ou un attribut) XSL apparait. S'il n'apparaît pas, l'élément xsl:if le fera à votre place. Je vais vous montrer ce que vous pouvez faire avec : imaginons que vous avez une page avec des documents (cet exemple est tiré de mes « tests » du projet TLDP-ES) et, à la lumière de ces documents, vous savez si les sources ont été convertis au format PDF, PS ou HTML. Comme ces informations se trouvent dans votre fichier XSL, vous pouvez vérifier si le fichier PDF a été généré et ajouter un lien vers celui-ci :


   <document>
     <title>Donantonio: bibliographic system for automatic distribuited publication. Specifications of Software Requeriments</title>
     <author>Ismael Olea</author>
         <author>Juan Jose Amor</author>
         <author>David Escorial</author>
         <module>donantonio</module>
         <format pdf="yes" ps="no" html="yes"/>
   </document>

Si l'attribut pdf de ce document a la valeur yes, comme dans cet exemple :


 <document>
     <title>Bellatrix Library and Semantic Web</title>
     <author>Daniel Guerrero</author>
         <module>bellatrix</module>
         <format pdf="yes" ps="yes" html="yes"/>
   </document>

Il placera alors un lien vers le document au format PDF. Si l'attribut est no ou toute autre valeur que vous permet la DTD, alors aucun lien ne sera placé. Si vous voulez vérifier, tous les documents XML et XSL, ils sont disponibles dans :


1.5. Instruction for-each

Si vous vérifiez le document XML de l'exemple ci-dessous, vous constaterez dans le premier document que nous avons trois auteurs séparés par une virgule. Une meilleure façon de séparer les auteurs serait de les insérer dans des balises <author> comme suit ::


   <document>
     <title>Donantonio: bibliographic system for automatic distribuited publication. Specifications of Software Requeriments</title>
     <author>Ismael Olea</author>
         <author>Juan Jose Amor</author>
         <author>David Escorial</author>
         <module>donantonio</module>
         <format pdf="yes" ps="no" html="yes"/>
   </document>

Pensez à faire un xsl:apply-templates et un xsl:template match pour placer chaque nom sur une ligne séparée. C'est possible, à condition d'utiliser l'instruction xsl:for-each.


 <xsl:for-each select="author">
            <tr>
               <td>
                      Author: <xsl:apply-templates />
           </td>
            </tr>
          </xsl:for-each>

Dans ce cas, le processeur parcourera tous les auteurs mentionnés dans le document et si vous vous demandez quel modèle j'ai créé pour traiter la partie des auteurs, je vous répondrai qu'il n'y en a aucun. Le processeur considérera l'élément apply-templates comme une instruction print du texte de l'élément sélectionné par l'élément for-each.


1.6. Instruction choose

Le dernier élément XSLT que je vous montrerai est l'élément choose. Il fonctionne comme le switch bien connu des langages courants comme le C.

Tout d'abord, vous devez déclarer un élément xsl:choose et placer ensuite toutes les options dans des éléments xsl:when. Si l'élément ne peut en satisfaire aucun, vous pouvez ajouter un élément xsl:otherwise :


 <xsl:variable name="even" select="position() mod 2"/>

  <xsl:choose>
     <xsl:when test="$even = 1">
          <![CDATA[<table width="100%" bgcolor="#cccccc">]]>
         </xsl:when>
     <xsl:when test="$even = 0">
             <![CDATA[<table width="100%" bgcolor="#99b0bf">]]>
         </xsl:when>
     <xsl:otherwise>
        <![CDATA[<table width="100%" bgcolor="#ffffff">]]>
         </xsl:otherwise>
  </xsl:choose>

L'instruction position() renvoie le nombre d'éléments traités. Dans le cas des documents, le nombre incrémentera autant de documents que vous aviez. Dans notre cas, nous souhaitons seulement savoir quel document est pair et impair, de façon à pouvoir créer un tableau avec des lignes d'une couleur pour les nombres pairs et d'une autre pour les nombres impairs. Je n'ai pris l'élément xsl:otherwise que pour illustrer son utilisation, mais je pense que ce ne sera jamais un tableau avec un fond blanc dans notre bibliothèque.

Si vous me demandez pourquoi j'ai ajouté une section CDATA, je vous répondrai que si vous le ne faites pas, alors le processeur demandera sa balise de fin (</table>), mais comme sa fin se trouve en bas, la balise de fin aura également besoin de la section CDATA.

Ici aussi je dois raccourcir le code. Si vous souhaitez le voir en entier, consultez ces documents :


2. Processeurs XSLT

2.1. Saxon

Saxon est un processeur XSLT écrit en Java dont j'utilise la version 6.5.2. Les instructions suivantes concernent donc cette version. Pour les autres versions, vérifiez soigneusement les informations pour lancer Saxon.


2.1.1. Installation

Après avoir téléchargé l'archive de saxon compressé avec zip, décompressez-la :


[danguer@perseo xslt]$ unzip saxon6_5_2.zip

Incluez ensuite le fichier saxon.jar dans votre chemin de classes. Vous pouvez passer le chemin du fichier jar à java avec l'option -cp path. Je placerai saxon.jar sous le répertoire xslt : il faut donc indiquer à java la classe que vous utilisez. Dans le cas de ma version de saxon (6.5.2), la classe est com.icl.saxon.StyleSheet. Il convient également de passer comme argument le document en XML et la feuille de style XSLT que vous utiliserez. Par exemple :


[danguer@perseo xslt]$ java -cp saxon.jar com.icl.saxon.StyleSheet document.xml tranformation.xsl

Cette commande enverra la sortie de la transformation vers la sortie standard, que vous pouvez envoyer vers un fichier avec :


[danguer@perseo xslt]$ java -cp saxon.jar com.icl.saxon.StyleSheet document.xml tranformation.xsl > file_processed.html

Par exemple, nous allons transformer notre premier exemple XSLT avec saxon ::


[danguer@perseo xslt]$ java -cp saxon.jar com.icl.saxon.StyleSheet cards.xml cards.xsl > cards.html

Et comme je l'ai indiqué, voici le résultat du traitement avec XSLT :


[danguer@perseo xslt]$ java -cp saxon.jar com.icl.saxon.StyleSheet hello.xml hello.xsl > hello.html

2.2. xsltproc

xsltproc est présent dans toutes les distributions majeures, sa syntaxe ressemble à celle de saxon :


[danguer@perseo xslt]$ xsltproc hello.xsl hello.xml > hello.html

Je sais qu'il existe d'autres processeurs XSLT, comme sablotron, mais comme je ne les ai pas utilisés, je ne peux pas vous les suggérer.


3. Références

J'essaie de finir mon Bachelor Degree à la BUAP (Benemérita Universidad Autónoma de Puebla) de Puebla (Mexique). Je suis impliqué dans le projet TLDP-ES, ce qui m'a permis d'apprendre tant de choses sur ces technologies. Maintenant, j'étudie le web sémantique.