version 1.51, dernière mise à jour le 15 février 2011.
Connues en anglais sous le nom de XSL patterns, les expressions de sélection sont des chaînes de caractères qui permettent de sélectionner des nœuds dans un document source. Il est également possible d'utiliser une syntaxe spéciale, appelée
XPath
, qui permet de faire référence à des éléments et/ou des attributs en se fondant sur la structure arborescente du document XML
(le Document Object Model -DOM).
L'exemple le plus simple d'expression de sélection est le nom d'un type d'élément. Cette expression sélectionne tous les éléments du type précisé descendant ou ancêtre d'un nœud donné. Exemple :
<xsl:value-of select="nom_element" />
L'opérateur /
permet de définir le chemin d'accès aux éléments à sélectionner, et donc leur parenté. Par exemple, section/paragraphe
sélectionne les éléments section
du nœud courant et pour chaque élément section
, sélectionne les éléments paragraphe
qu'il contient. En d'autres termes, cette expression sélectionne les petits-fils paragraphe
du nœud courant qui ont pour père un nœud section
.
Un nom d'élément peut être remplacé par *
dans une expression. Par exemple, */paragraphe
sélectionne tous les petits-fils paragraphe
quel que soit leur père.
L'utilisation de //
permet d'appliquer la recherche aux descendants et non pas seulement aux fils directs. Par exemple, section//paragraphe
sélectionne tous les éléments paragraphe
descendant d'un élément section
fils direct du nœud courant.
Le caractère .
sélectionne le nœud courant. Par exemple, .//paragraphe
sélectionne tous les descendants paragraphe
du nœud courant.
La chaîne ..
sélectionne le père du nœud courant. Par exemple, ../paragraphe
sélectionne tous les nœuds paragraphe
frères du nœud courant.
Considérons par exemple le fichier XML
suivant, l’élément courant étant l’élément racine A
:
<A>
<B>
<C>C1</C>
</B>
<B>
<C>C2</C>
</B>
<D>
<C>C3</C>
</D>
<E>
<B>
<C>C4</C>
</B>
</E>
<B>
<E>
<C>C5</C>
</E>
</B>
</A>
*/C
renvoie les éléments C1, C2 et C3
B/C
renvoie les éléments C1 et C2
B//C
renvoie les éléments C1, C2 et C5
.//B/C
renvoie les éléments C1, C2 et C4
L'expression comment()
sélectionne tous les nœuds commentaires fils du nœud courant.
L'expression text()
sélectionne tous les nœuds fils du nœud courant, ne contenant que du texte.
L'expression node()
sélectionne tous les nœuds fils du nœud courant.
L'expression id("UnIdentifiant")
sélectionne l'élément, normalement unique, qui a un attribut attr
de type ID (cf. le paragraphe "Déclaration d'attribut de type ID") valant "UnIdentifiant"
.
On peut aussi « naviguer » dans les branches de l'arborescence du document XML
, en utilisant les ressources du DOM. Différents types de syntaxes sont possibles, fondées sur une expression de la forme Element[Expression]
. Par exemple :
elt[i]
où i
est un nombre entier désigne le i
-ème descendant direct d'un même parent ayant le nom indiqué. Par exemple, paragraphe[3]
désigne le 3ème enfant de l'élément courant, portant le nom paragraphe
. Attention, la numérotation commence à 1 et non à 0.
elt[position()>i]
où i
est un nombre entier sélectionne tous les éléments précédés d'au moins i
éléments de même nom comme descendants du même parent. Par exemple, paragraphe[position()>5]
sélectionne tous les éléments paragraphe
dont le numéro d'ordre est strictement supérieur à 5.
elt[position() mod 2=1]
sélectionne tout élément qui est un descendant impair.
elt[souselt]
sélectionne tout élément elt
qui a au moins un descendant souselt
(à ne pas confondre avec elt/souselt
, qui sélectionne tout élément souselt
ayant pour parent elt
...).
elt[first-of-any()]
sélectionne le premier élément elt
fils de l'élément courant.
elt[last-of-any()]
sélectionne le dernier élément elt
fils de l'élément courant.
elt[first-of-type()]
sélectionne l'élément elt
fils de l'élément courant, s'il est premier de son type. Considérons l'exemple suivant. L'élément elt
peut contenir des nœuds de type texte elt1
et elt2
dans n'importe quel ordre. On cherche à évaluer l'expression elt2[first-of-type()]
. Deux cas se présentent : soit elt
commence par au moins un élément elt1
, puis éventuellement un élément elt2
, soit il commence par un élément elt2
. L'expression ne sélectionne le premier élément elt2
que dans le second cas, où il n'est précédé par aucun élément de même type.
elt[last-of-type()]
sélectionne de même l'élément elt
fils de l'élément courant, s'il est le dernier de son type.
Par exemple, l'expression section/paragraphe[last-of-type() and first-of-type()]
sélectionne les éléments paragraphe
fils uniques dont le père est un élément section
; l'expression section/paragraphe[last-of-any() and first-of-any()]
sélectionne les éléments paragraphe
dont le père est un élément section
qui ne contient qu'un seul élément paragraphe
.
La fonction ancestor()
permet la sélection d'un ancêtre du nœud courant. Elle reçoit en argument une expression de sélection et recherche le premier ancêtre du nom correspondant à la sélection. Par exemple, ancestor(chapitre)/titre
sélectionne l'élément titre
du chapitre contenant l'élément courant.
Les attributs d'un élément sont sélectionnés en faisant précéder leur nom par le caractère @
. Les règles relatives à la sélection des éléments s'appliquent également aux attributs :
section[@titre]
sélectionne les éléments section
qui ont un attribut titre
.
section[@titre="Introduction"]
sélectionne les éléments section
dont l'attribut titre
a pour valeur Introduction
.
Si l'on veut afficher le contenu de l'attribut, on le fait précéder du caractère /
. Par exemple, <xsl:value-of select="paragraphe/@titre" />
permet l'affichage du titre de l'élément paragraphe fils de l'élément courant (si rien n'est précisé, par défaut il s'agit du premier élément paragraphe fils).
Les opérateurs logiques not()
, and
et or
peuvent être utilisés, comme par exemple section[not(@titre)]
, qui sélectionne les éléments section
qui n'ont pas d'attribut titre
. Attention : lorsque, dans la DTD
par exemple, l'attribut est défini comme ayant une valeur par défaut, même s'il n'est pas explicité dans le document XML
, il est considéré comme existant.
Comme son nom l'indique, XPath
est une spécification fondée sur l'utilisation de chemin d'accès permettant de se déplacer au sein du document XML
. Dans ce but, un certain nombre de fonctions ont été définies. Elles permettent de traiter les chaînes de caractères, les booléens et les nombres.
Le XPath
établit un arbre de nœuds correspondant au document XML
. Les types de nœuds peuvent être différents : nœud d'élément, nœud d'attribut et nœud de texte. En vue d'une utilisation plus aisée, le XPath
comprend un mécanisme qui associe à tous ces types une chaîne de caractères.
La syntaxe de base du XPath
est fondée sur l'utilisation d'expressions. Une expression peut s'appliquer à quatre types d'objets :
un ensemble non ordonné de nœuds ;
une valeur booléenne (vrai ou faux) ;
un nombre en virgule flottante ;
une chaîne de caractères.
Chaque évaluation d'expression dépend du contexte courant. Une des expressions les plus importantes dans le standard XPath
est le chemin de localisation. Cette expression sélectionne un ensemble de nœuds à partir d'un nœud contextuel.
Un chemin de localisation peut être de type absolu ou relatif.
Dans le cas où il est de type absolu, il commence toujours par le signe /
indiquant la racine du document XML
;
Dans le cas où il est de type relatif, le nœud de départ est le nœud contextuel courant.
La syntaxe de composition d'un chemin de localisation peut être de type abrégé ou non abrégé. Toute syntaxe non abrégée ne trouve pas forcément d'équivalent en syntaxe abrégée.
Un chemin de localisation est composé de trois parties :
un axe, définissant le sens de la relation entre le nœud courant et le jeu de nœuds à localiser;
un nœud spécifiant le type de nœud à localiser;
0 à n prédicats permettant d'affiner la recherche sur le jeu de nœuds à récupérer.
Par exemple, dans le chemin child::section[position()=1]
, child
est le nom de l'axe, section
le type de nœud à localiser (élément ou attribut) et [position()=1]
est un prédicat. Les doubles ::
sont obligatoires.
La syntaxe d'une localisation s'analyse de gauche à droite. Dans notre cas, on cherche dans le nœud courant, un nœud section qui est le premier nœud de son type.
child
: contient les enfants directs du nœud contextuel.
descendant
: contient les descendants du nœud contextuel. Un descendant peut être un enfant, un petit-enfant...
parent
: contient le parent du nœud contextuel, s'il y en a un.
ancestor
: contient les ancêtres du nœud contextuel. Cela comprend son père, le père de son père... Cet axe contient toujours le nœud racine, excepté dans le cas où le nœud contextuel serait lui-même le nœud racine.
following-sibling
: contient tous les nœuds cibles du nœud contextuel. Dans le cas où ce nœud est un attribut ou un espace de noms, la cible suivante est vide.
preceding-sibling
: contient tous les prédécesseurs du nœud contextuel ; si le nœud contextuel est un attribut ou un espace de noms, la cible précédente est vide.
following
: contient tous les nœuds du même nom que le nœud contextuel, situés après le nœud contextuel dans l'ordre du document, à l'exclusion de tout descendant, des attributs et des espaces de noms.
preceding
: contient tous les nœuds du même nom que le nœud contextuel, situés avant lui dans l'ordre du document, à l'exclusion de tout descendant, des attributs et des espaces de noms.
attribute
: contient les attributs du nœud contextuel ; l'axe est vide quand le nœud n'est pas un élément.
namespace
: contient tous les nœuds des espaces de noms du nœud contextuel ; l'axe est vide quand le nœud contextuel n'est pas un élément.
self
: contient seulement le nœud contextuel.
descendant-or-self
: contient le nœud contextuel et ses descendants.
ancestor-or-self
: contient le nœud contextuel et ses ancêtres. Cet axe contiendra toujours le nœud racine.
Le contenu d'un prédicat est une prédiction. Chaque expression est évaluée et le résultat est un booléen.
Par exemple, section[3]
est équivalent à section[position()=3]
.
Ces deux expressions entre crochets sont équivalentes : chacune d'entre elles produit un booléen. Dans le premier cas, il n'y a pas de test, on sélectionne simplement le troisième élément, l'expression est obligatoirement vraie. Dans le second cas, un test est effectué par rapport à la position de l'élément section
; lorsque la position sera égale à 3, l'expression sera vraie.
Cette syntaxe va être présentée par plusieurs exemples... Les parties qui dépendent du fichier XML
analysé sont écrites
de cette manière
.
child::
para
: sélectionne l'élément
para
enfant du nœud contextuel.
child::*
: sélectionne tous les éléments enfants du nœud contextuel.
child::text()
: sélectionne tous les nœuds de type texte du nœud contextuel.
child::node()
: sélectionne tous les enfants du nœud contextuel, quel que soit leur type.
attribute::
name
: sélectionne tous les attributs
name
du nœud contextuel.
attribute::
*
: sélectionne tous les attributs du nœud contextuel.
descendant::
para
: sélectionne tous les descendants
para
du nœud contextuel.
ancestor::
div
: sélectionne tous les ancêtres
div
du nœud contextuel.
ancestor-or-self::
div
: sélectionne tous les ancêtres
div
du nœud contextuel et le nœud contextuel lui-même si c'est un
div
.
descendant-or-self::
para
: sélectionne tous les descendants
para
du nœud contextuel et le nœud contextuel lui-même si c'est un
para
.
self::
para
: sélectionne le nœud contextuel si c'est un élément
para
, et rien dans le cas contraire.
child::
chapitre
/descendant::
para
: sélectionne les descendants
para
de l'élément
chapitre
enfant du nœud contextuel.
child::*/child::
para
: sélectionne tous les petits-enfants
para
du nœud contextuel.
/child::
: sélectionne l'élément racine du document.
/descendant::
para
: sélectionne tous les éléments
para
descendants du document contenant le nœud contextuel.
/descendant::
olist
/child::
item
: sélectionne tous les éléments
item
qui ont un parent
olist
et qui sont dans le même document que le nœud contextuel.
child::
para
[position()=1]
: sélectionne le premier enfant
para
du nœud contextuel.
child::
para
[position()=last()]
: sélectionne le dernier enfant
para
du nœud contextuel.
child::
para
[position()=last()-1]
: sélectionne l'avant-dernier enfant
para
du nœud contextuel.
child::
para
[position()1]
: sélectionne tous les enfants
para
du nœud contextuel autres que le premier.
following-sibling::
para
[position()=1]
: sélectionne le prochain
para
frère du nœud contextuel.
On pourrait continuer encore longtemps cette liste d'exemples. Cette syntaxe non-abrégée permet beaucoup de raffinement. child::*[self::
chapitre
or self::
sstitre
][position()=last()]
permet ainsi de sélectionner le dernier enfant
chapitre
ou
sstitre
du nœud contextuel.
Cette syntaxe recoupe en fait la « syntaxe de base » vue plus haut. Elle permet d'obtenir des expressions du type para[@type="avertissement"][5]
, qui sélectionne le cinquième enfant de l'élément para
, parmi ceux qui ont un attribut type
ayant la valeur avertissement
.
De nombreuses fonctions peuvent être utilisées. Ces fonctions concernent quatre catégories d'objets : nœuds, chaînes de caractères, booléens et nombres. Chaque fonction peut avoir zéro ou plusieurs arguments. Dans les descriptions suivantes, lorsqu'un élément est suivi du caractère ?
, cela signifie qu'il est optionnel. Cette liste est loin d'être exhaustive. Le chapitre suivant présente les fonctions XPath de manière plus complète.
Fonctions retournant un nombre :
last()
: retourne un nombre égal à l'index du dernier nœud dans le contexte courant.
position()
: retourne un nombre égal à la position du nœud dans le contexte courant.
Fonction retournant un jeu de nœuds : id(objet)
, permet de sélectionner les éléments par leur identifiant.
Beaucoup de fonctions existent. Citons pour mémoire notamment :
string(nœud?)
: cette fonction convertit un objet en chaîne de caractères selon les règles suivantes :
un ensemble de nœuds est converti en chaîne de caractères en retournant la valeur textuelle du premier nœud de l'ensemble dans l'ordre du document. Si l'ensemble des nœuds est vide, une chaîne vide est retournée.
un nombre est converti en chaîne suivant des règles dépendant de sa nature (NaN
, nombre entier, non-entier, zéro...).
la valeur booléenne false
est convertie en chaîne de caractères "false"
, de même pour la valeur booléenne true
.
Cette fonction n'a pas pour objet de convertir des nombres en chaînes de caractères pour les présenter aux utilisateurs ; il existe des fonctions de transformations XSLT
pour ce faire (format-number
et xsl:number
)
concat(chaine1, chaine2, chaine*)
: retourne une chaîne résultant de la compilation des arguments
string-length(chaine?)
: cette fonction retourne le nombre de caractères de la chaîne. Dans le cas où l'argument est omis, la valeur retournée est égale à la longueur de la valeur textuelle du nœud courant
Outre la fonction logique not()
, ainsi que les fonctions true()
et false()
, une fonction utile est lang(chaine)
. Elle teste l'argument chaine
par rapport à l'attribut xml:lang
du nœud contextuel ou de son plus proche ancêtre dans le cas où le nœud contextuel ne contient pas d'attribut de ce type. La fonction retourne true
si l'argument est bien la langue utilisée ou si la langue utilisée est un sous-langage de l'argument (par exemple, en//us
). Sinon elle retourne false
.
Voici quelques fonctions de manipulations de nombres :
floor(nombre)
: retourne le plus grand entier inférieur à l'argument passé à la fonction.
ceiling(nombre)
: retourne le plus petit entier supérieur à l'argument passé à la fonction.
round(nombre)
: retourne l'entier le plus proche de l'argument passé à la fonction.
Dans cette partie, nous allons détailler quelques éléments XSLT
et présenter des exemples d'utilisation. Ne sera introduite ici qu'environ la moitié des éléments de formatage XSLT
; libre à vous de vous renseigner sur les éléments manquants.
Le nom de domaine utilisé pour les exemples est celui de la spécification 1.0 : http://www.w3.org/TR/xslt. La spécification
XSLT 2.0
a été publiée en janvier 2007, mais les modifications qu'elle introduit sortent du cadre restreint de ce cours.
Un fichier XSL
doit commencer par les lignes indiquant le numéro de version XML
et l'encodage de caractères utilisé :
<?xml version="1.0" encoding="ISO-8859-1"?>
Sont présentés ici les éléments de base du document XSL
, ceux que l'on retrouve dans l'immense majorité des cas, voire la totalité pour certains.
Cet élément doit être l'élément racine du document XSL
, et en tant que tel doit figurer juste après le prologue (et les commentaires éventuels qui suivraient celui-ci). Il contient tous les autres éléments de mise en forme. Dans le cas général, l'utilisation de cet élément est de la forme :
<xsl:stylesheet id="id" version="nombre" xmlns:pre="URI">
(...)
</xsl:stylesheet>
id
est l'identifiant unique de la feuille de style. version
est le numéro de version de la feuille de style XSLT
. À l'heure actuelle, la version peut être 1.0
, 1.1
ou 2.0
. xmlns:pre
correspond à la définition de l'espace de noms. pre
indique le préfixe qui sera utilisé dans la feuille de style pour faire référence à l'URI de l'espace nominal. Exemples :
<xsl:stylesheet version="1.0" xmlns:xsl="uri:xsl">
(...)
</xsl:stylesheet>
... permet d'avoir accès uniquement à des fonctions de base.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
(...)
</xsl:stylesheet>
... permet d'avoir accès à des fonctions évoluées d'XSLT
. C'est cette déclaration que nous allons utiliser dans la suite.
Cet élément vide, à placer comme premier enfant de xsl:stylesheet
, permet de spécifier des options concernant l'arbre de sortie. L'utilisation de cet élément est de la forme :
<xsl:output method="xml | html | text" version="nmtoken" encoding="chaine" omit-xml-declaration="yes | no" standalone="yes | no" doctype-public="chaine" doctype-system="chaine" cdata-section-elements="elt" indent="yes | no" media-type="chaine" />
method
identifie la méthode de transformation. Dans le cas où elle est égale à text
, aucune mise en forme n'est effectuée.
version
identifie la version de la méthode de sortie (xml1.0, html4.01...).
encoding
indique la version du jeu de caractères à utiliser pour la sortie.
omit-xml-declaration
indique au processeur XSLT s'il doit ajouter ou non une déclaration XML.
standalone
indique au processeur XSLT s'il doit créer un arbre de sortie avec ou sans déclaration de type de document.
doctype-public
indique l'identifiant public utilisé par la DTD
associée à la transformation.
doctype-system
indique l'identifiant system utilisé par la DTD
associée à la transformation.
cdata-section-elements
indique les éléments dont le contenu doit être traité lors de la transformation via une section CDATA
.
indent
présente la transformation sous forme d'arbre dans le cas où la valeur de cet attribut est égale à yes
.
media-type
indique le type MIME des données résultantes de la transformation.
Par exemple :
<xsl:output method="html" version="html4.01" encoding="ISO-8859-1" doctype-public="-//W3C//DTD HTML 4.01//EN" doctype-system="http://www.w3.org/TR/html4/strict.dtd" />
... permet d'indiquer que le fichier de sortie sera au format HTML 4.01, conforme à la DTD
publique de l'HTML
du W3C, et que le jeu de caractères utilisé est l'ISO-8859-1. Le résultat en sortie est un fichier HTML
commençant par
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
... et possédant dans son head
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
L'utlisation d'un tel élément permet donc de se dispenser de la spécification explicite du codage de caractères via un élément <meta>
lors de l'écriture de la feuille de style XSL
.
Cet élément définit un modèle à appliquer à un nœud et à un contexte spécifiques. L'utilisation de cet élément est de la forme :
<xsl:template name="nommodele" match="expression" mode="modemodele">
</xsl:template>
name
correspond au nom associé au modèle.
match
indique quel jeu de nœuds sera affecté par le modèle. Cette expression peut comprendre un test d'existence d'attribut, le caractère |
indiquant que le modèle s'applique à un élément ou à un autre, ainsi que tout élément permettant de définir un jeu d'attributs.
mode
permet à un élément d'avoir plusieurs modèles, chacun générant une sortie différente.
Nous reviendrons plus loin sur les possibilités qu'offre cet élément pour une conception modulaire de la feuille de style.
Cet élément permet d'insérer la valeur d'un nœud dans la transformation. Ce nœud est évalué en fonction d'une expression. Cette expression peut correspondre à un élément, à un attribut ou à tout autre nœud contenant une valeur. L'utilisation de cet élément est de la forme :
<xsl:value-of select="expression" disable-output-escaping="yes | no" />
La valeur de select
est évaluée et c'est cette évaluation qui sera insérée dans la transformation.
disable-output-escaping
agit sur la transformation des caractères. Dans le cas où sa valeur est yes
, la chaîne <
est insérée dans la transformation en tant que signe <
, ce qui peut entraîner des erreurs. Dans le cas contraire, la chaîne <
est insérée telle quelle dans la transformation.
Cet élément insère un nouvel élément dans la transformation. Le nom de l'élément est donné par l'attribut name
. L'utilisation de cet élément est de la forme :
<xsl:element name="nomelement" use-attribute-sets="jeuattr">
</xsl:element>
name
correspond au nom de l'élément à créer. use-attribute-sets
correspond au jeu d'attributs à associer à l'élément créé. Par exemple :
<xsl:element name="p">
<xsl:value-of select="texte" />
</xsl:element>
... permet de créer dans le fichier HTML
un élément de paragraphe renfermant le contenu de l'élément texte
du document XML
.
Cet élément définit un attribut et l'ajoute au dernier élément actuellement ouvert dans le résultat de la transformation. Son utilisation est de la forme :
<xsl:attribute name="nom">valeur</xsl:attribute>
name
correspond au nom de l'attribut à ajouter dans le contexte courant. valeur
correspond à la valeur à lui donner. Par exemple :
<image>
<xsl:attribute name="src">test.gif</xsl:attribute>
</image>
... permet d'ajouter à l'élément image
l'attribut src
et de lui affecter la valeur test.gif
, ce qui a pour effet de produire en sortie l'élément suivant :
<image src="test.gif">
</image>
On aurait pu, de manière tout à fait équivalente, écrire
<xsl:element name="image">
<xsl:attribute name="src">test.gif</xsl:attribute>
</xsl:element>
... ce qui a pour effet de produire en sortie
<image src="test.gif">
</image>
Il est possible de créer des éléments de manière plus compacte, à l'aide d'une syntaxe particulière. Supposons par exemple que nous ayons dans un fichier XML
l'élément <image source="test.gif" texte_alternatif="Image de test"/>
, et que nous souhaitions obtenir dans le fichier de sortie l'élément <img src="test.gif" alt="Image de test"/>
. Il existe deux manières :
La syntaxe longue, avec...
<xsl:element name="img">
<xsl:attribute name="src">
<xsl:value-of select="@source" />
</xsl:attribute>
<xsl:attribute name="alt">
<xsl:value-of select="@texte_alternatif" />
</xsl:attribute>
</xsl:element>
La syntaxe courte, utilisant des accolades...
<img src="{@source}" alt="{@texte_alternatif}" />
La seconde syntaxe est plus compacte ; mais elle présente deux inconvénients :
Dès lors que des expressions XPath sont un peu longues, cette syntaxe complique la relecture de la feuille de style ;
Une feuille XSL
est avant tout un fichier XML
. En tant que tel, on peut souhaiter sa transformation ou son traitement automatisé. La syntaxe longue s'y prête plus facilement.
Cet élément de bouclage, que l'on a déjà rencontré, crée une boucle dans laquelle sont appliquées des transformations. Son utilisation est de la forme :
<xsl:for-each select="jeunœud">
</xsl:for-each>
select
correspond au jeu de nœuds devant être parcouru par la boucle. Exemple d'utilisation :
<ul>
<xsl:for-each select="item">
<li>
<xsl:value-of select="." />
</li>
</xsl:for-each>
</ul>
Cet élément permet d'effectuer un tri sur un jeu de nœuds. Il doit être placé soit dans un élément xsl:for-each
soit dans un élément xsl:apply-templates
. C'est un élément vide qui peut être appelé plusieurs fois pour effectuer un tri multicritères. Chaque appel à cet élément provoque un tri sur un champ spécifique, dans un ordre prédéfini. L'utilisation de cet élément est de la forme :
<xsl:sort select="nœud" data-type="text | number | elt" order="ascending | descending" lang="nmtoken" case-order="upper-first | lower-first" />
select
permet de spécifier un nœud comme clé de tri.
data-type
correspond au type des données à trier. Dans le cas où le type est number
, les données sont converties puis triés.
order
correspond à l'ordre de tri. Cet attribut vaut ascending
ou descending
.
lang
spécifie quel jeu de caractères utiliser pour le tri ; par défaut, il est déterminé en fonction des paramètres système.
case-order
indique si le tri a lieu sur les majuscules ou minuscules en premier, selon qu'il prend la valeur upper-first
ou lower-first
.
Par exemple :
<ul>
<xsl:for-each select="livre">
<xsl:sort select="auteur" order="descending" />
<li>
<b>
<xsl:value-of select="auteur" />
</b>
<br />
<xsl:value-of select="titre" />
</li>
</xsl:for-each>
</ul>
Dans cet exemple, la liste des livres est classée dans l'ordre alphabétique décroissant des noms d'auteur.
Cet élément permet d"insérer un nombre formaté pouvant servir de compteur. L'utilisation de cet élément est de la forme :
<xsl:number level="single | multiple | any" count="nœud" from="nœud" value="expression" format="chaine" lang="nmtoken" grouping-separator="car" grouping-size="nombre" />
level
indique quels niveaux doivent être sélectionnés pour le comptage : single
permet de ne compter que la position du nœud par rapport à ses frères ; multiple
permet de compter la position du nœud par rapport à ses ancêtres (par exemple de la forme 1.3.4) ; enfin any
renvoie la position du nœud par rapport à l'ensemble des nœuds de même nom dans tout le document.
count
indique quels nœuds doivent être comptés dans les niveaux sélectionnés ; dans le cas où cet attribut n'est pas défini, les nœuds comptés sont ceux ayant le même type que celui du nœud courant.
from
identifie le nœud à partir duquel le comptage commence.
value
indique l'expression correspondant à la valeur du compteur ; si cet attribut n'est pas défini, le nombre inséré correspond à la position du nœud (position()
).
format
spécifie le format de l'affichage du nombre ; cela peut être un chiffre, un caractère (a-z, A-Z) et comprendre un caractère de séparation tel que le point (.
), le trait d'union (-
) ou autre. Les formats possibles sont "1", "01", "a", "A", "i", "I".
lang
spécifie le jeu de caractères à utiliser ; par défaut, il est déterminé en fonction des paramètres du système.
grouping-separator
identifie le caractère permettant de définir la séparation entre les centaines et les milliers.
grouping-size
spécifie le nombre de caractères formant un groupe de chiffres dans un nombre long ; le plus souvent la valeur de cet attribut est 3. Ce dernier attribut fonctionne avec...
...grouping-separator
; si l'un des deux manque, ils sont ignorés.
Exemple d'utilisation :
<ul>
<xsl:for-each select="livre">
<xsl:sort select="auteur" />
<xsl:number level="any" from="/" format="1." />
<li>
<b>
<xsl:value-of select="auteur" />
</b>
<br />
<xsl:value-of select="titre" />
</li>
</xsl:for-each>
</ul>
Cet élément permet la fragmentation du modèle dans certaines conditions. Il est possible de tester la présence d'un attribut, d'un élément, de savoir si un élément est bien le fils d'un autre, de tester les valeurs des éléments et attributs. L'utilisation de cet élément est de la forme :
<xsl:if test="condition">action</xsl:if>
test
prend la valeur 1
ou 0
suivant le résultat de la condition (vrai ou faux). action
correspond à l'action devant être effectuée (texte à afficher, second test, gestion de chaîne...). Exemples d'utilisation : <xsl:if test="@âge=’24’>Cette personne a 24 ans.</xsl:if>
, ou :
<xsl:if test="livre">
<ul>
<xsl:for-each select="livre">
<li>
<b>
<xsl:value-of select="auteur" />
<br />
</b>
<xsl:value-of select="titre" />.<xsl:if test="@langue='français'">Ce livre est en français.</xsl:if>
</li>
</xsl:for-each>
</ul>
</xsl:if>
Dans le code précédent, si l'attribut langue
de l'élément livre
vaut français
, le processeur ajoutera au fichier de sortie la phrase "Ce livre est en français". Il ne se passe rien si ce n'est pas le cas.
Cet élément permet de définir une liste de choix et d'affecter à chaque choix une transformation différente. Chaque choix est défini par un élément xsl:when>
et un traitement par défaut peut être spécifié grâce à l'élément xsl:otherwise
. Exemple d'utilisation :
<ul>
<xsl:for-each select="livre">
<li>
<b>
<xsl:value-of select="auteur" />
<br />
</b>
<xsl:value-of select="titre" />
<xsl:choose>
<xsl:when test="@langue='francais'">Ce livre est en français.</xsl:when>
<xsl:when test="@langue='anglais'">Ce livre est en anglais.</xsl:when>
<xsl:otherwise>Ce livre est dans une langue non répertoriée.</xsl:otherwise>
</xsl:choose>
</li>
</xsl:for-each>
</ul>
Il est possible en XSLT
de définir des variables et des paramètres permettant de faire des calculs. Il est nécessaire pour cela de faire appel à deux éléments XSL : xsl:variable
et xsl:param
.
L'élément xsl:variable
sert à créer les variables dans XSLT. Il possède les attributs suivants :
name
: cet attribut est obligatoire. Il spécifie le nom de la variable.
select
: expression XPath
qui spécifie la valeur de la variable.
Par exemple :
<xsl:variable name="nombre_livres" select="255" />
<xsl:variable name="auteur" select="'Victor Hugo'" />
<xsl:variable name="nombre_pages" select="livre/tome/@page" />
On notera la présence des guillemets imbriqués quand il s'agit d'affecter une chaîne de caractères à une variable.
La portée d'une variable est limitée aux éléments-frères et à leurs descendants. Par conséquent, si une variable est déclarée dans une boucle xsl:for-each
ou un élément xsl:choose
ou xsl:if
, on ne peut s'en servir en-dehors de cet élément.
Une variable est appelée en étant précédée du caractère $
: <xsl:value-of select="$nombre_pages" />
.
On peut utiliser une variable pour éviter la frappe répétitive d'une chaîne de caractères, et/ou faciliter la mise à jour de la feuille de style.
L'élément xsl:template
peut être appelé indépendamment d'une sélection d'un nœud. Pour cela, il faut renseigner l'attribut name
, et l'appeler à l'aide de l'élément xsl:call-template
. Par exemple
<xsl:template name="separateur">
<hr />
<img src="ligne.gif" alt="séparation" width="150" height="2" />
<hr />
</xsl:template>
Il suffit alors de l'appeler avec <xsl:call-template name="separateur"/>
.
Les paramètres créés avec ces deux éléments sont habituellement utilisés dans les modèles nommés, que nous venons de voir. Ils permettent de passer des valeurs aux modèles. Un paramètre est créé avec l'élément <xsl:param>
, et passé à un modèle avec l'élément xsl:with-param
. Les deux ont deux attributs :
name
, obligatoire, qui donne un nom au paramètre ;
select
, une expression XPath
facultative permettant de donner une valeur par défaut au paramètre.
Par exemple, on peut imaginer un template
permettant d'évaluer le résultat d'une expression polynômiale :
<xsl:template name="polynome">
<xsl:param name="variable_x" />
<xsl:value-of select="2*$variable_x*$variable_x+(-5)*$variable_x+2" />
</xsl:template>
Il suffit alors de l'appeler en lui passant diverses valeurs pour le paramètre variable_x
pour qu'il évalue cette expression. Ce comportement se rapproche de celui d'une fonction dans un langage de programmation. Par exemple...
<xsl:call-template name="polynome">
<xsl:with-param name="variable_x" select="3.4" />
</xsl:call-template>
... permet d'afficher le résultat de 2*3.4^2-5*3.4+2. On remarquera que :
la soustraction d'un nombre se fait par addition du nombre (négatif) opposé ;
la division se fait non par le caractère /
, mais par l'opérateur div
.
Les feuilles de style que nous avons écrites jusqu'à présent conviennent à de petits projets. Cependant, lorsqu'un développement doit être réalisé à plusieurs, ou lorsqu'il devient nécessaire de revenir après quelque temps sur du code déjà écrit, la possibilité de mettre au point des feuilles de style modulaires prend toute sa valeur. Deux étages successifs le permettent :
Au sein d'une même feuille de style, la séparation du traitement de chaque nœud peut se faire en généralisant l'emploi des templates ;
Il est possible de scinder une feuille de styles en plusieurs fichiers, inclus les uns dans les autres.
Les feuilles de style que nous avons écrites jusqu'à présent utilisaient l'élément xsl:for-each
pour « inspecter » successivement tous les éléments renvoyés par une expression XPath. Lorsque le document XML
initial ou le document de sortie deviennent un peu complexes, cette méthode aboutit à l'écriture d'une feuille de style consistant en de multiples boucles imbriquées. Une solution commode est alors de séparer les traitements pour chaque nœud du document initial grâce aux éléments xsl:template
.
Revenons à la deuxième feuille de style que nous avions écrite pour présenter la liste des bouteilles :
<?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>Exemple de sortie HTML</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
</head>
<body>
<h1>Bouteille de marque <xsl:value-of select="bouteille/marque" />
</h1>
<h2>Composition:</h2>
<h3>Ions positifs</h3>
<ul>
<xsl:for-each select="bouteille/composition/ion_positif">
<li>
<xsl:value-of select="." />
</li>
</xsl:for-each>
</ul>
<h3>Ions négatifs</h3>
<ul>
<xsl:for-each select="bouteille/composition/ion_negatif">
<li>
<xsl:value-of select="." />
</li>
</xsl:for-each>
</ul>
<h3>Autres matériaux</h3>
<ul>
<xsl:for-each select="//autres_materiaux">
<li>
<xsl:value-of select="." />
</li>
</xsl:for-each>
</ul>
<h2>Lieu d'origine</h2>
<p>Ville de <b>
<xsl:value-of select="bouteille/source/ville" />
</b>, dans le département <b>
<xsl:value-of select="bouteille/source/departement" />
</b>
</p>
<h2>Autres informations</h2>
<ul>
<li>Contenance: <xsl:value-of select="bouteille/contenance" />
</li>
<li>pH: <xsl:value-of select="bouteille/ph" />
</li>
</ul>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Modifions-la dans un premier temps afin qu'elle puisse s'adapter à un document XML
listant plusieurs bouteilles :
<?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>Exemple de sortie HTML</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
</head>
<body>
<xsl:for-each select="//bouteille">
<h1>Bouteille de marque <xsl:value-of select="bouteille/marque" />
</h1>
<h2>Composition:</h2>
<h3>Ions positifs</h3>
<ul>
<xsl:for-each select="bouteille/composition/ion_positif">
<li>
<xsl:value-of select="." />
</li>
</xsl:for-each>
</ul>
<h3>Ions négatifs</h3>
<ul>
<xsl:for-each select="bouteille/composition/ion_negatif">
<li>
<xsl:value-of select="." />
</li>
</xsl:for-each>
</ul>
<h3>Autres matériaux</h3>
<ul>
<xsl:for-each select="//autres_materiaux">
<li>
<xsl:value-of select="." />
</li>
</xsl:for-each>
</ul>
<h2>Lieu d'origine</h2>
<p>Ville de <b>
<xsl:value-of select="bouteille/source/ville" />
</b>, dans le département <b>
<xsl:value-of select="bouteille/source/departement" />
</b>
</p>
<h2>Autres informations</h2>
<ul>
<li>Contenance: <xsl:value-of select="bouteille/contenance" />
</li>
<li>pH: <xsl:value-of select="bouteille/ph" />
</li>
</ul>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Les imbrications de boucles xsl:for-each
peuvent devenir lourdes à gérer. Il est plus facile de maintenir la structure suivante :
<?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" version="html4.01" encoding="ISO-8859-1" doctype-public="-//W3C//DTD HTML 4.01//EN" doctype-system="http://www.w3.org/TR/html4/strict.dtd" />
<xsl:template match="/">
<html>
<head>
<title>Exemple de sortie HTML</title>
</head>
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="bouteille">
<h1>Bouteille de marque <xsl:value-of select="marque" />
</h1>
<h2>Composition:</h2>
<h3>Ions positifs</h3>
<ul>
<xsl:for-each select="composition/ion_positif">
<xsl:apply-templates select="." />
</xsl:for-each>
</ul>
<h3>Ions négatifs</h3>
<ul>
<xsl:for-each select="composition/ion_negatif">
<xsl:apply-templates select="." />
</xsl:for-each>
</ul>
<h3>Autres matériaux</h3>
<ul>
<xsl:for-each select="autres_materiaux">
<xsl:apply-templates select="." />
</xsl:for-each>
</ul>
<h2>Lieu d'origine</h2>
<p>Ville de <b>
<xsl:value-of select="source/ville" />
</b>, dans le département <b>
<xsl:value-of select="source/departement" />
</b>
</p>
<h2>Autres informations</h2>
<ul>
<li>Contenance: <xsl:value-of select="contenance" />
</li>
<li>pH: <xsl:value-of select="ph" />
</li>
</ul>
</xsl:template>
<xsl:template match="ion_positif">
<li>
<xsl:value-of select="." />
</li>
</xsl:template>
<xsl:template match="ion_negatif">
<li>
<xsl:value-of select="." />
</li>
</xsl:template>
<xsl:template match="autres_materiaux">
<li>
<xsl:value-of select="." />
</li>
</xsl:template>
</xsl:stylesheet>
Le gain n'est pas très appréciable ici, mais si vous jetez un œil à la feuille de style qui figure en lien en bas de cette page, vous verrez un exemple d'utilisation plus complexe... et plus utile.
Il est parfois nécessaire de scinder une feuille de style en plusieurs fichiers différents :
Cela permet d'en faciliter la maintenance, en rendant possible un travail collectif ;
Cela permet aussi de créer des bibliothèques de modèles pouvant être réutilisés d'un projet à un autre
Pour cela, il suffit de spécifier dans la feuille de style « maîtresse » un appel à une feuille de style autre, soit par l'élément xsl:include
soit par l'élément xsl:import
. Par exemple...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:include href="feuille_incluse.xsl" />
</xsl:stylesheet>
Les modèles et variables définis dans la feuille de style ainsi incluse sont directement incorporés à la feuille de style appelante. Quand on utilise xsl:import
, s'il y a conflit entre une déclaration de template dans la feuille appelante et la feuille appelée, c'est la première qui est prioritaire. La syntaxe est la même :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="feuille_incluse.xsl" />
</xsl:stylesheet>
Cette création est mise à disposition par Gilles Chagnon sous un contrat Creative Commons.