XSL

XSL (eXtensible Stylesheet Language) est un langage de style complet pour réordonner, dupliquer, changer la structure des éléments et construire des pages complexes (avec colonnes, tables des matières, etc). Il permet une présentation des données xml en (x)html ou en pdf (via xslfo).

Les feuilles de transformation XSLT sont des documents xml, régis par un schéma.
On leur donne l’extension .xslt ou .xsl.


Appliquer une feuille de style

En shell

xsltproc feuille.xsl document.xml > resultat.xml

Dans le navigateur

En ajoutant la référence à la feuille de style à l’entête du fichier xml

<?xml-stylesheet type="text/xsl" href="feuille.xsl"?>

Transformation XML par XSL

Lors de la transformation XML par XSL, le document XML est parcouru dans l’ordre. A chaque noeud XML (élément) rencontré, le système vérifie si un template dans le fichier XSL peut s’appliquer

Les noeuds texte non traités sont copiés dans le résultat

document.xml :

<?xml version="1.0"?>
<?xml:stylesheet type="text/xsl" href="style.xsl"?>
<biblio>
  <book lang="fr">
    <title>XML</title>
    <auteur>Jean Martin</auteur>
  </book>
  <book lang="en">
    <title>XML in action</title>
    <author>John Smith</author>
  </book>
  <paper>
    <title>XML software</title>
    <date>05/02/2002</date>
  </paper>
</biblio>

style.xsl :

<xsl:stylesheet version='1.0'
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <!-- règle de production pour <book> -->
  <xsl:template match="book">
    <p>a book</p>
  </xsl:template>
  <!-- règle de production pour <book lang="en"> -->
  <xsl:template match="book[lang='en']">
    <p>a book in english </p>
  </xsl:template>
  <!-- règle de production pour pour <title> -->
  <xsl:template match="title">
    <p>a title</p>
  </xsl:template>
</xsl:stylesheet>

Résultat de la transformation :

<p>a book</p>
<p>a book in english </p>
<p>a title</p>
05/02/2002

Structure du document XSL

Prologue

Tout comme tout document XML, une feuille de style XSL commence par un prologue

<?xml version="1.0"?>

Namespaces

Suivit de la déclaration des espaces de nommages

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

Templates

Après l’entête, se situe des templates XSL, qui correspondent à un ou des noeuds de l’arbre source.
Les templates contiennent des instructions XSL, des balises et du contenu à produire dans le document cible.
On peut cibler les noeuds sélectionnés avec XPath (syntaxe de sélection XML).

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

  <!-- S'applique la 1ere <section> -->
  <xsl:template match="section[1]">
    <p>Titre : <xsl:value-of select="title"/></p>
    <xsl:apply-templates select="authors"/>
  </xsl:template>

  <!-- S'applique à tous les <authors> -->
  <xsl:template match="authors">
    <p><xsl:value-of select="."/></p>
  </xsl:template>
</xsl:stylesheet>

À l’intérieur d’un template, le noeud courant est le noeud de l’arbre reconnu par l’attribut match.
Les commandes XSL contenues dans le template peuvent également utiliser des expressions Xpath pour sélectionner des noeuds XML. Les expressions Xpath peuvent être absolues (racine de l’arbre source) ou relatives au noeud courant, mais la portée du noeud courant est le template pour lequel il est définit.


Afficher une valeur

value-of

Permet de récupérer le contenu des feuilles texte d’un sous-arbre

style.xsl :

<xsl:template match="compte">
  <p><xsl:value-of select="client"/></p>
  <p><xsl:value-of select="solde"/></p>
</xsl:template>

document.xml :

<compte devise="euro">
  <numero>123456KL</numero>
  <client>
    <nom>Dupont</nom>
    <prenom>Michel</prenom>
  </client>
  <solde>1683.25</solde>
</compte>

Résultat :

<p>DupontMichel</p>
<p>1683.25</p>

Les accolades ({...}) est l’équivalent de <xsl:value-of select="..."/>

text

Permet d’écrire explicitement un noeud texte
Isole le texte, pour ne pas reproduire les retours chariots

Avec text Écrit directement
<xsl:template match="client">
  <x>
    <xsl:text>Client : </xsl:text>
    <xsl:value-of select="name"/>
  </x>
</xsl:template>
<xsl:template match="client">
  <x>Client :
    <xsl:value-of select="name"/>
  </x>
</xsl:template>
Client: Dupont
Client:
    Dupont

Par défaut, le texte est échappé

<xsl:text>coût &lt; 100€ </xsl:text>
<xsl:text disable-output-escaping="yes">coût &lt; 100€ </xsl:text>

number

Permet de formatter des nombres et de numéroter des éléments.

<xsl:template match="chapter">
  <xsl:variable name="chapNum">
    <xsl:number count="chapter" format="1" level="any"/>
  </xsl:variable>
  <xsl:value-of select="$chapNum"/>
</xsl:template>

Attributs :

count Expression XPath qui spécifie les éléments à compter
level Définit les niveaux de l'arborescence à prendre en compte dans la numérotation

single compte les éléments sur un niveau uniquement
multiple compte les éléments sur plusieurs niveaux
any compte tous les éléments qui matchent count
from Expression XPath qui définit quand commmencer le comptage
value Expression XPath ou valeur littérale. Doit retourner un nombre.
format Définit le format du nombre généré

format="1"1 2 3 4 5 6 7 8 9 10 11
format="01"01 02 03 04 ... 09 10 11 ... 99 100 101
format="a"a b c d e f ... x y z aa ab ac
format="A"A B C D E F ... X Y Z AA AB AC
format="i"i ii iii iv v vi vii viii ix x
format="I"I II III IV V VI VII VIII IX X
Peut être agrémenté de tout autre caractère : format="1. ", format="1) "
lang Définit le langage dont l'alphabet doit être utilisé
letter-value alphabetic (par défaut) ou traditional. Spécifie si la numérotation dans le langage choisit est alphabétique (ex nombres romains) ou traditionnelle (valeurs numériques)
grouping-separator Définit quel caractère doit être utilisé pour séparer les groupes de chiffres (, par défaut)
grouping-size Spécifie combien de chiffres sont dans un groupe (3 par défaut)

level

document.xml :

<items>
   <item>
       <item>Alice</item>
       <item>Bob</item>
       <item>Carole</item>
   </item>
   <item>
       <item>Dave</item>
       <item>Erin</item>
       <item>Frank</item>
   </item>
   <item>
       <item>Gena</item>
       <item>Heather</item>
       <item>Isabel</item>
   </item>
</items>

table.xsl :

<xsl:template match="item">
   <xsl:for-each select="item">
      <xsl:element name="item">
          <!-- single -->
          <xsl:number format="1. " level="single" />
          <xsl:value-of select="."/>
          <!-- multiple -->
          <xsl:number format="1. " level="multiple" />
          <xsl:value-of select="."/>
          <!-- any -->
          <xsl:number format="1. " level="any" />
          <xsl:value-of select="."/>
          <!-- position() -->
          <xsl:number format="1. " value="position()" />
          <xsl:value-of select="."/>
      </xsl:element>
   </xsl:for-each>
</xsl:template>

Resultat :

Single     Multiple     Any         position()

1. Alice   1.1. Alice   2. Alice    1. Alice
2. Bob     1.2. Bob     3. Bob      2. Bob
3. Carole  1.3. Carole  4. Carole   3. Carole

1. Dave    2.1. Dave    6. Dave     1. Dave
2. Erin    2.2. Erin    7. Erin     2. Erin
3. Frank   2.3. Frank   8. Frank    3. Frank

1. Gena    3.1. Gena    10. Gena    1. Gena
2. Heather 3.2. Heather 11. Heather 2. Heather
3. Isabel  3.3. Isabel  12. Isabel  3. Isabel

from

<xsl:template match="item">
   <xsl:for-each select="item">
      <xsl:element name="item">
          <xsl:number format="1. " level="single" from="*[position()>1]" />
          <xsl:value-of select="."/>
      </xsl:element>
   </xsl:for-each>
</xsl:template>
. Alice
. Bob
. Carole
1. Dave
2. Erin
3. Frank
1. Gena
2. Heather
3. Isabel

value

<xsl:template match="item">
   <xsl:variable name="parent" select="count(preceding-sibling::*)"/>
   <xsl:for-each select="item">
      <xsl:element name="item">
          <xsl:number format="1. " level="any" value="1 + count(preceding::*) - $parent" />
          <xsl:value-of select="."/>
      </xsl:element>
   </xsl:for-each>
</xsl:template>
1. Alice
2. Bob
3. Carole
4. Dave
5. Erin
6. Frank
7. Gena
8. Heather
9. Isabel

grouping-separator

<xsl:number value="250000" grouping-separator="."/>
250.000

Appliquer des templates

apply-templates

Permet d’appliquer les templates à la descendance d’un noeud auquel est appliqué un template (la descendance d’un noeud auquel est appliqué un template n’est pas traitée par défaut).

Si la cible de apply-templates n’est pas précisée, la commande s’applique sur tous les fils du noeud courant. Sinon, on peut sélectionner les noeuds cibles avec XPath.

<xsl:apply-templates select="//para"/>
<xsl:apply-templates select="para[@class='title']"/>

style.xsl :

<xsl:template match="compte">
  <p>
    Propriétaire du compte :
    <xsl:apply-templates select="client"/>
  </p>
  </xsl:template>
  <xsl:template match="client">
    <b><xsl:value-of select="nom"/></b>,
    <xsl:value-of select="prénom"/>
</xsl:template>

document.xml :

<compte devise="euro">
  <numero>123456KL</numero>
  <client>
    <nom>Dupont</nom>
    <prenom>Michel</prenom>
  </client>
  <solde>1683.25</solde>
</compte>

Résultat :

<p>
Propriétaire du compte :
<b>Dupont</b>,
Michel
</p>

modes

Placer un attribut mode sur apply-templates applique le template avec ce même attribut. Cela permet de regrouper des templates pour définir des traitements particuliers.

<xsl:template match="chapter">
  <div class="sommaire">
    <ul>
      <xsl:apply-templates select= "section" mode="sommaire"/>
    </ul>
  </div>
  <xsl:apply-templates select="section"/>
</xsl:template>

<xsl:template match="section" mode="sommaire"/>
  <li><xsl:value-of select="title"/></li>
</xsl:template>

<xsl:template match="section">
  <div class="section"><xsl:apply-templates/></div>
</xsl:template>

call-template

Permet de regrouper dans un template des règles de production utilisées fréquemment

<xsl:template match='member'>
  <xsl:call-template name="info "/>
  ...
</xsl:template>

<xsl:template match='guest'>
   <xsl:call-template name="info "/>
   ...
</xsl:template>

<xsl:template name='info'>
  Prénom : <xsl:value-of select="firstname"/>
  Nom : <xsl:value-of select="lastname"/>
</xsl:template>

Insérer des éléments

comment

Permet d’insérer un commentaire <!-- ... -->

<xsl:comment> ... </xsl:comment>

attribute

Permet de générer explicitement un attribut pour l’élément en cours de production

<xsl:template match="link">
  <a>
    <xsl:attribute name="title">
      <xsl:value-of select= "linktitle"/>
    </xsl:attribute>
    <xsl:apply-templates/>
  </a>
</xsl:template>

attribute-set

Permet de définir un groupe d’attribus

<xsl:attribute-set name="link-spec">
  ...
</xsl:attribute-set>

element

Permet de générer explicitement un élément

<xsl:template match="link[@dest='spec']">
  <xsl:element name="a" use-attribute-sets= "link-spec">
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

L’attribut use-attribute-sets permet de référencer les groupes d’attributs à produire, séparés par des espaces

copy

Copier le noeud courant :

Permet de copier un noeud de l’arbre source

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

L’attribut use-attribute-sets permet de référencer les groupes d’attributs à produire, séparés par des espaces

Copier un attribut :

Permet de copier un attribut de l’arbre source

<xsl:template match="link">
  <a>
    <xsl:copy select="./@href"/>
    <xsl:apply-templates/>
  </a>
</xsl:template>

copy-of

Permet de copier un sous-arbre de l’arbre source

<xsl:template match="div[@class= 'menu']">
  <xsl:copy-of select= ". "/>
</xsl:template>

Inclure un document

La fonction document() permet d’inclure un document xml tiers.

<xsl:copy-of select="document('labels.xml')">

On peut aussi appliquer les templates sur un document tiers :

<xsl:variable name='gurl'>
  http://www.google.fr/search?q=<xsl:value-of select="$keyword"/>
</xsl:variable>
<xsl:apply-templates select="document($gurl)//table[2]">

Boucles et conditions

for-each

Permet de boucler sur les noeuds d’un ensemble de noeuds

<xsl:template match="addressbook">
  <xsl:for-each select="contact/name">
    <xsl:value-of select="firstname"/>
    <xsl:text> <xsl:text>
    <xsl:value-of select="lastname"/>
  </xsl:for-each>
</xsl:template>

if

Permet de traiter une condition
Il est parfois préférables d’utiliser deux templates

<xsl:template match="a">
  <xsl:copy>
    <xsl:attribute name="href">...</xsl:attribute>
    <xsl:if test="not(start-with(@href,'http://example.fr')">
      <xsl:text>(lien externe)</xsl:text>
    </xsl:if>
    <xsl:apply-templates>
  </xsl:copy>
</xsl:template>

choose

Conditions multiples

<xsl:choose>
  <xsl:when test="condition 1">
    ...
  </xsl:when>
  <xsl:when test="condition 2">
    ...
  </xsl:when>
  <xsl:otherwise>
    ...
  </xsl:otherwise>
</xsl:choose>

sort

Permet de spécifier l’ordre d’application d’une instruction sur un ensemble de noeuds
S’applique sur apply-templates et for-each

<xsl:for-each select="catalog/cd">
  <xsl:sort select="artist"/>
  <tr>
    <td><xsl:value-of select="title"/></td>
    <td><xsl:value-of select="artist"/></td>
  </tr>
</xsl:for-each>

Attributs :

select Critère de tri
lang Pour les tris alphabétiques
data-type text ou number
order ascending ou descending
case-order upper-first ou lower-first

Variables et paramètres

variable

Permet de définir une variable (non modifiable).
La valeur de la variable peut être le résultat d’une expression XPath ou un texte littéral

<xsl:variable name="position" select="count(active)"/>
<xsl:variable name="date">27/07/1969</xsl:variable>

Les variables ainsi définies peuvent ensuite être utilisées dans des expressions XPath.
On obtient leur valeur avec $ :

<xsl:apply-templates select="item[$position]"/>
<xsl:value-of select="$date"/>

Une variable déclarée dans un template est une variable locale, dont la portée est limitée au template.

param

La valeur d’une variable reste constante, tandis que la valeur d’un paramètre est une valeur par défaut, qui peut être modifiée avec with-param lors de l’appel à call-template ou apply-templates.

<xsl:param name="COLOR" select="'black'"/>

<xsl:template match="item">
  <font color="{$COLOR}">
    <xsl:apply-templates/>
  </font>
</xsl:template>

  ...
  <xsl:apply-templates select="item">
    <xsl:with-param name="COLOR" select="'blue'"/>
  </xsl:apply-templates>
  ...

Options de sortie

output

L’élément output permet de paramétrer les options de sortie de la feuille de style

Attributs :

method xml, text ou html
version nmtoken
encoding string
omit-xml-declaration yes ou no
standalone yes ou no
doctype-public string
doctype-systel string
cdata-section-elements qnames
indent yes ou no
media-type string

Clés

key

L’élément key permet de créer un tableau associatif valeur/noeud. L’attribut match définit les noeuds concernés et l’attribut use permet de définir la valeur (la clé du tableau).

<xsl:key name="mykeys" match="footnote[@id]" use="@id"/>

On peut récupérer le noeud associé à une valeur donnée avec la fonction key(name, value)

Cette fonction est souvent utilisée avec generate-id(node), qui génère un id unique pour un noeud donné (ou le noeud courant si omis)

document.xml :

<body>
   <p>Lorem ipsum dolor sit amet <ref footnote="1" /></p>

   <footnotes>
     <footnote id="1">Ma référence</footnote>
   </footnotes>
</body>

style.xsl :

<xsl:output method="html" /> 

<!-- Keep body and p as is -->
<xsl:template match="body|p">
  <xsl:copy>
    <xsl:apply-templates />
  </xsl:copy>
</xsl:template>

<!-- Key map of footnotes -->
<xsl:key name="mykeys" match="footnote[@id]" use="@id"/>

<!-- Replace <ref footnote="id"> with link to footnote -->
<xsl:template match="ref[@footnote]">
  <a href="#{generate-id(key('mykeys', @footnote))}">
    <xsl:for-each select="key('mykeys', @footnote)">
      <sup>(<xsl:number/>)</sup>
    </xsl:for-each>
  </a>
</xsl:template>

<!-- Footnotes list -->
<xsl:template match="footnotes">
  <ul>
    <xsl:for-each select="footnote">
      <li>
        <a name="{generate-id()}">
          <xsl:number format="1. " />
          <xsl:value-of select="."/>
        </a>
      </li>
    </xsl:for-each>
  </ul>
</xsl:template>

Résultat :

<body>
   <p>Lorem ipsum dolor sit amet <a href="#id0xffffffffffc12300"><sup>(1)</sup></a></p>

   <ul><li><a name="id0xffffffffffc12300">1. Ma référence</a></li></ul>
</body>

Modularisation des XSLT

Une feuille de style .xsl peut inclure d’autres feuilles de style .xsl.
Deux méthodes sont disponibles: include et import.

Include

include permet d’inclure le contenu d’une autre feuille de style .xsl.
Une feuille inclue a la même précédence que la feuille parente. Si les deux feuilles de style définissent un même template, le template le plus en bas de la page est appliqué.

main.xsl :

<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes"/>

<xsl:template match="/">
  <xsl:for-each select="COLLECTION/BOOK">
    <xsl:apply-templates select="TITLE"/>
    <xsl:apply-templates select="AUTHOR"/>
    <xsl:apply-templates select="PUBLISHER"/>
  </xsl:for-each>
</xsl:template>

<!-- Le template suivant est overridé.
Il pourrait overrider doc.xsl s'il était placé en-dessous de include -->
<xsl:template match="TITLE">Title: <xsl:value-of select="."/></xsl:template>

<xsl:include href="doc.xsl" />
</xsl:stylesheet>

doc.xsl :

<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="TITLE">Title - <xsl:value-of select="."/></xsl:template>
  <xsl:template match="AUTHOR">Author - <xsl:value-of select="."/></xsl:template>
  <xsl:template match="PUBLISHER">Publisher - <xsl:value-of select="."/></xsl:template>
</xsl:stylesheet>

Import

import permet d’importer des templates. Pour appliquer les templates importés, il faut les appeler explicitement à l’intérieur d’un template de la feuille de style avec apply-imports.

main.xsl :

<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="doc.xsl"/>

<xsl:template match="example">
  <div style="border: solid red">
    <xsl:apply-imports/>
  </div>
</xsl:template>
</xsl:stylesheet>

doc.xsl :

<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="example">
    <pre><xsl:apply-templates/></pre>
  </xsl:template>
</xsl:stylesheet>

Résultat :

<div style="border: solid red"><pre>...</pre></div>

Messages

message

Permet de produire des messages au cours de la transformation, soit

<xsl:template match="section">
  <xsl:message>
    <xsl:text>Traitement de la section</xsl:text>
    <xsl:value-of select="title"/>
  </xsl:message>
  ...
</xsl:template>

Namespaces

Si le document source utilise plusieurs namespaces, ils doivent tous être déclarés dans la feuille de style et utilisé dnas les balises produits et les XPath.

<xsl:stylesheet version="1.0"
                xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
                xmlns:fo="http://www.w3.org/1999/XSL/Format"
                xmlns:html= "http://www.w3.org/1999/xhtml">
...
<xsl:template match="html:body">
  <fo:flow>
  ...
  </fo:flow>
</xsl:template>
</xsl:stylesheet>