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.
<?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 :
<?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>
<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
<?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>
<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 :
<?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>
<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 :
<?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.
<?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>
<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()
<
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.
<?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>
<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 :
<?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>
<?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>
<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
<?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>
<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.
<?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>
<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.
<?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>
<?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>