Valeur, boucle et élément courant en XSLT 1.0

erwan amoureux

On essaiera ici, de décrire les premiers concepts importants du XSLT. Comment afficher une valeur, comment créer une boucle, ce qu'est un élément courant et son importance. Remerciements à gangsoleil et à jérome pour la correction orthographique.

- Titre : Valeur, boucle et élément courant en XSLT 1.0

- Auteur : erwan amoureux

- Parution : 25 septembre 2004

- Mise à jour : 12 juillet 2020

- Licence : Copyright ® 2006 Erwan Amoureux. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Valeur, boucle et élément courant

- I-A. value-of : affichage des valeurs

- I-A-1. Déclaration

- I-A-2. Comportement suivant les xpath

- I-B. Définition de l'élément courant

- I-C. for-each : la boucle

- I-C-1. Déclaration et fonctionnement

- I-C-2. Modification et utilisation de l'élément courant

- I-C-3. Comportement de parcours

- I-D. Complément sur l'élément courant

- I-D-1. XPath sur l'élément courant

- I-D-2. Différence entre . et current()

I - Valeur, boucle et élément courant

I-A - value-of : affichage des valeurs

I-A-1 - Déclaration

Cette déclaration, primordiale puisqu'elle est la seule qui permet l'affichage d'une valeur en xslt, ne peut se faire que dans un template. Comme les templates sont un chapitre suivant, les exemples déroulés ci-après se tiendront tous dans le tout premier template

<xsl:template match="/">
<xsl:value-of select="'Cela affiche une string!'"/>
</xsl:template>

L'attribut select de xsl:value-of contient la donnée à afficher. Cette donnée peut être de la forme d'une chaîne (comme ci-dessus), d'un nombre ou d'un chemin xpath.

I-A-2 - Comportement suivant les xpath

Pour ce paragraphe nous prendrons un xml « annuaire » comme exemple.

fichier xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="monfichier.xslt"?>
<annuaire>
<personne>
<nom>Dupond</nom>
<prenom>Marc</prenom>
<num_telephone>02 96 45 87 34</num_telephone>
</personne>
<personne>
<nom>Dupont</nom>
<prenom>Matthias</prenom>
<num_telephone>03 45 67 25 99</num_telephone>
</personne>
<personne>
<nom>Durand</nom>
<prenom>Lucien</prenom>
<num_telephone>01 45 29 62 77</num_telephone>
</personne>
</annuaire>

Le xsl:value-of quand il a un xpath dans son attribut select affiche les données text() (les données uniquement textuelles qui se trouvent entre les balises) correspondantes. Si nous souhaitons par exemple afficher les données de la deuxième personne « Dupont Matthias » sur une ligne de tableau HTML, on utilisera le code suivant :

fichier xslt:

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title></title>
</head>
<body>
<table>
<tr>
<td>
<xsl:value-of select="/annuaire/personne[2]/nom"/>
</td>
<td>
<xsl:value-of select="/annuaire/personne[2]/prenom"/>
</td>
<td>
<xsl:value-of select="/annuaire/personne[2]/num_telephone"/>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

résultat

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body
><table>
<tr><td>Dupont</td><td>Matthias</td><td>03 45 67 25 99</td>
</tr>
</table>
</body>
</html>

Ceci correspond bien a chacun des text() des trois balises nom, prenom et num_telephone. Quel est le comportement sur un xpath qui sélectionne plusieurs nœuds ? Nous allons prendre tous les noms du fichier : //nom

fichier xslt

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title></title>
</head>
<body>
<table>
<tr>
<td>
<xsl:value-of select="//nom"/>
</td>

</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

résultat

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<table>
<tr>
<td>Dupond</td>
</tr>
</table>
</body>
</html>

La réponse est simple, si un xpath sélectionne plusieurs nœuds seul le premier est affiché. Que se passerait-il par contre si le xpath sélectionné était celui d'un nœud qui en contient d'autres, comme les balises personnes ? Et bien ce serait le contenu text() de toutes les balises contenues qui s'afficherait :

fichier xslt

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title></title>
</head>
<body>
<table>
<tr>
<td>
<xsl:value-of select="/annuaire/personne[2]"/>
</td>

</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

résultat

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<table>
<tr>
<td>DupontMatthias03 45 67 25 99</td>
</tr>
</table>
</body>
</html>

Attention, je rappelle que le contenu d'un attribut n'est pas un text(). Si le fichier xml était modifié de cette façon :

fichier xml modifié:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="monfichier.xslt"?>
<annuaire>
<personne>
<nom id="1">Dupond</nom>
<prenom>Marc</prenom>
<num_telephone>02 96 45 87 34</num_telephone>
</personne>
<personne>
<nom  id="2">Dupont</nom>
<prenom>Matthias</prenom>
<num_telephone>03 45 67 25 99</num_telephone>
</personne>
<personne>
<nom  id="3">Durand</nom>
<prenom>Lucien</prenom>
<num_telephone>01 45 29 62 77</num_telephone>
</personne>
</annuaire>

En lui appliquant le fichier xslt précédent, le résultat ne serait pas modifié. La seule méthode pour afficher le contenu d'un attribut est d'utiliser le xpath correspondant:@nom_de_l_attribut.

variation sur le fichier xslt

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title></title>
</head>
<body>
<table>
<tr>
<td>
<xsl:value-of select="/annuaire/personne[2]/nom/@id"/>
</td>
<td>
<xsl:value-of select="/annuaire/personne[2]/nom"/>
</td>
<td>
<xsl:value-of select="/annuaire/personne[2]/prenom"/>
</td>
<td>
<xsl:value-of select="/annuaire/personne[2]/num_telephone"/>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

résultat

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<table>
<tr>
<td>2</td>
<td>Dupont</td>
<td>Matthias</td>
<td>03 45 67 25 99</td>
</tr>
</table>
</body>
</html>

I-B - Définition de l'élément courant

L'élément courant est le nœud sélectionné par le processeur, il n'est pas forcément celui qui est en cours de lecture ou d'affichage ; il ne peut être créé/modifié que par les instructions xsl:for-each ou les xsl:apply-templates . Dans ces boucles, il est le nœud en court de traitement, on peut l'atteindre par la fonction xslt: current()

<xsl:value-of select="current()"/>

La suite de ce cours va en détailler une partie, et le chapitre Tests et Conditions complétera ses particularités comportementales.

I-C - for-each : la boucle

I-C-1 - Déclaration et fonctionnement

L'élément xsl:for-each est une boucle traitant tous les nœuds xml qui lui sont soumis, car en XSLT il n'existe pas de boucle indexée(for i=.. to ..) ; la boucle s'effectue toujours sur l'intégralité des données fournies dans le select du xsl:for-each Comme pour le xsl:value-of le chemin xpath lui est fourni dans son attribut select . Le code xslt encadré par les balises xsl:for-each sera donc appliqué sur chaque élément « visité » : l'élément courant current()

exemple de déclaration

<xsl:for-each select="//node()">
<xsl:value-of select="current()"/>

</xsl:for-each>

I-C-2 - Modification et utilisation de l'élément courant

Nous allons à nouveau sélectionner tous les noms, mais cette fois-ci, au lieu de mettre le xpath dans le xsl:value-of, nous le soumettrons au xsl:for-each.

mon fichier xslt

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title></title>
</head>
<body>
<table>
<tr>
<xsl:for-each select="//nom">
<td>
<xsl:value-of select="current()"/>
</td>
</xsl:for-each>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

résultat:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title />
</head>
<body>
<table>
<tr>
<td>Dupond</td>
<td>Dupont</td>
<td>Durand</td>
</tr>
</table>
</body>
</html>

Cette fois-ci nous pouvons observer que tous les nœuds ont bien été traités et que l'élément courant est bien le nœud en cours de lecture dans le xpath du select.

I-C-3 - Comportement de parcours

Des tests précédents nous pouvons déjà déduire une partie du parcours des xpath. En effet, dans l'exemple précédent , le premier nom affiché est le premier rencontré en partant du haut du fichier. En nous servant du xsl:for-each et du xpath //node() qui sélectionne TOUS les nœuds du xml. Nous allons pouvoir voir l'ordre de parcours en utilisant un xml aux balises numérotées :

mon fichier xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="monfichier.xslt"?>
<racine num="0">
<noeud num="1">
<noeud num="3">
<noeud num="5"/>
<noeud num="6"/>
</noeud>
<noeud num="4">
<noeud num="7"/>
</noeud>
</noeud>
<noeud num="2"/>
</racine>

le fichier xslt

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/">
<html>
<body>
<xsl:for-each select="//node()">
<xsl:value-of select="concat(@num,' :: ')"/>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

résultat

<html xmlns:fo="http://www.w3.org/1999/XSL/Format">
<body> :: 0 :: 1 :: 3 :: 5 :: 6 :: 4 :: 7 :: 2 :: </body>
</html>

L'ordre est donc :

- a) sens vertical du haut vers le bas prendre le premier aller en b) ;

- b) traiter le nœud aller en c) ;

- c) si le nœud a un fils :
- prendre le premier dans le sens vertical,
- retour en b),
- sinon aller en d) ;

- d) si dans le sens vertical il y a un « frère » à lui succéder, aller en b) sinon en e) ;

- e) je remonte au père du nœud s'il existe et aller en d) sinon aller en f) ;

- f) fin.

attention le premier :: du résultat n'est pas une erreur . C'est simplement que le tout premier nœud sélectionner est : ?xml-stylesheet type=« text/xsl » href=« mon fichier.xslt »? comme il n'a pas d'attribut num, rien n'est affiché.

I-D - Complément sur l'élément courant

I-D-1 - XPath sur l'élément courant

Jusqu'ici nous avons utilisé des chemins relatifs précédés de / (où il faut indiquer tout le chemin depuis la racine ) ou de // pour parser l'intégralité de l'arbre. Avec l'apparition de l'élément courant, nous allons pouvoir le prendre comme référent au lieu de la racine. Les XPath l'utilisant comme tel ne sont précédés ni de / ni de //. Dans cet exemple, reprenant le fichier d'annuaire, on parcourt les différentes personnes et on affiche leur nom prenom, num_telephone via l'élément courant fixé par le xsl:for-each

fichier xslt

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title/>
</head>
<body>
<table>
<xsl:for-each select="/annuaire/personne">
<tr>
<td>
<xsl:value-of select="nom"/>
</td>
<td>
<xsl:value-of select="prenom"/>
</td>
<td>
<xsl:value-of select="num_telephone"/>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

résultat

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8" >
<title />
</head>
<body>
<table>
<tr>
<td>Dupond</td>
<td>Marc</td>
<td>02 96 45 87 34</td>
</tr>
<tr>
<td>Dupont</td>
<td>Matthias</td>
<td>03 45 67 25 99</td>
</tr>
<tr>
<td>Durand</td>
<td>Lucien</td>
<td>01 45 29 62 77</td>
</tr>
</table>
</body>
</html>

Pour des soucis de performances, il est à noter que se servir de l'élément courant comme base des xpath(comme ici), quand cela est possible, est toujours plus performant que d'utiliser des chemins préfixés / ou //.

I-D-2 - Différence entre . et current()

Le « . » est un raccourci pour le xpath self::* et non pas pour la fonction xslt current(). Comme nous l'avons vu précédemment current() renvoie l'élément en cours de traitement.Self::* lui renvoie l'élément en cours de lecture. Par contre il est vrai que dans certains cas,comme dans le xsl:value-of ces éléments pointent sur le même nœud.

fichier xslt:

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title/>
</head>
<body>
<table>
<xsl:for-each select="/annuaire/personne/nom">
<tr>
<td>
<xsl:value-of select="current()"/>
</td>
<td>
<xsl:value-of select="."/>
</td>

</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

résultat:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8" >
<title />
</head>
<body>
<table>
<tr>
<td>Dupond</td>
<td>Dupond</td>
</tr>
<tr>
<td>Dupont</td>
<td>Dupont</td>
</tr>
<tr>
<td>Durand</td>
<td>Durand</td>
</tr>
</table>
</body>
</html>

La différence se fait essentiellement dans les évaluations de xpath, lorsqu'on utilise des tests. Nous allons dans un premier xsl:for-each nous placer sur le nom de la première personne. //personne[1]/nom,suivent trois xsl:for-each imbriqués qui auront comme base commune de remonter de deux parents puis de parser à nouveau toutes les balises nom en effectuant un test.
Dans le premier select=« parent::*/parent::*/personne[nom=current()]/nom », on ne sélectionne que les personnes dont le fils nom est égal au nœud courant.
Dans le second select=« parent::*/parent::*/personne/nom[text()=.] », on ne sélectionne que les balises nom dont le contenu est égal à l'élément en cours de lecture (elle-même).
Dans le dernier select=« parent::*/parent::*/personne/nom[.=current()] », on ne sélectionne que les balises en lecture dont le contenu est égal à l'élément courant.

fichier xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title/>
</head>
<body>
<table>
<xsl:for-each select="//personne[1]/nom">
<tr>
<xsl:for-each select="parent::*/parent::*/personne[nom=current()]/nom">

<td>
<xsl:value-of select="."/>
</td>

</xsl:for-each>
</tr>    <tr>
<xsl:for-each select="parent::*/parent::*/personne/nom[text()=.]">

<td>
<xsl:value-of select="."/>
</td>

</xsl:for-each>
</tr>    <tr>
<xsl:for-each select="parent::*/parent::*/personne/nom[.=current()]">

<td>
<xsl:value-of select="."/>
</td>

</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

résultat

<?xml-stylesheet type="text/xsl" href="C:\Documents and Settings\amoureux erwan\Mes documents\xml\tutoriel\boucle\test.xslt"?>
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title />
</head>
<body>
<table>
<tr>
<td>Dupond</td>
</tr>
<tr>
<td>Dupond</td>
<td>Dupont</td>
<td>Durand</td>
</tr>
<tr>
<td>Dupond</td>
</tr>
</table>
</body>
</html>