Qu'est ce que le XSLT ?

Le XSLT pour Extensible Stylesheet Language Transformations. Est comme son nom l'indique un langage de mise en forme conditionnelle. Le XSLT, sert à réaliser des transformations grâce à une ou plusieurs feuilles de style d'un jeu de données en XML vers un autre format (exemples non exhaustifs  : XML,XHTML,HTML, Csv, Text,Pdf, etc.). Le XSLT est donc un moteur de templating et c'est pourquoi les feuilles de styles sont parfois appelées "modèle". Le XSLT permet également de compiler des fragments de codes en internes lors de sa résolution.

L'exigence technique du XSLT

Le XSLT est un outil particulièrement puissant, souvent ignoré, car considéré comme trop complexe par ses détracteurs. Hors que ce soit pour des projets web, ou pour des clients lourds, le XSLT est un outil de premier choix dans la manipulation de données et la génération de document à partir d'une source XML. Il invite à une structuration claire et standardisée répondant aux normes les plus exigeantes du W3C.

Sommaire de cette publication sur le XSLT

  1. Les moteurs d'interprétations
  2. Les possibilités
  3. Structure minimale
  4. Comment inclure un fichier XSLT ?
  5. Comment créer un template en XSLT ?
  6. Comment s'utilise XPATH ?
  7. Comment créer une variable en XSLT ?
  8. Comment afficher le contenu d'un element ?
  9. Comment créer une boucle ?
  10. Comment créer une condition ?
  11. Comment créer un modèle réutilisable ?
  12. Comment appeler un modèle réutilisable ?

Les moteurs d'interprétations ou processeur de transformation XSLT

Le XSLT nécessite un moteur d'interprétation (un processeur) pour réaliser les transformations de ces modèles. L'ensemble des langages modernes permettent de « compiler » une feuille de style XSLT et un XML grâce à des classes natives.

  1. Le Javascript avec l'objet XSLTProcessor
  2. Le PHP et la classe XSLTProcessor
  3. Le C# et l'objet XslCompiledTransform

Ce point est particulièrement intéressant, ainsi une même feuille de style XSLT pourra être utilisée sur des projets à différents niveaux, tout en conservant la même logique de traitement pour l'affichage.

Qu'elles sont les possibilités offertes par le XSLT

Comme expliqué plus haut dans cet article, les possibilités du XSLT sont relativement "sans limite". Mais qu'elles sont exactement les limites du XSLT ?

  1. Inclure des fichiers XSLT dans un autre.
  2. De créer des fragments indépendants de template.
  3. D'assigner des valeurs à des variables.
  4. D'effectuer des contrôles conditionnels (avec if et switch).
  5. De réaliser des boucles (foreach et for i).
  6. De faire des appels récursifs.
  7. De concaténer des données.
  8. De cumuler la résolution des feuilles de styles.
  9. D'exécuter des fonctions de script (c#,ecma,php...) déconseillé car discutable sur le plan sécuritaire
  10. De sortir toute sorte de format de données après compilation.

Structure minimale d'un fichier XSLT

Un fichier XSLT commence toujours par une balise stylesheet ouvrante/fermante. Elle dispose de l'attribut version qui permet de définir quel version de compilation est supporté par le moteur employé.

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

</xsl:stylesheet>

Cette balise est toujours suivie d'un élément autoformant output. Cette balise sert à définir différentes informations pour le format du fichier de sortie par le biais de plusieurs attributs dont les principaux sont :

method  (xml | html | text) : qui sert à définir le format de restitution de la donnée après traitement.

omit-xml-declaration (yes | no) : sert à indiquer au moteur si il doit inclure ou non une déclaration XML (ex : <?xml version="1.0"?>) dans le corps du document qui sera généré.

encoding : le format d'encodage de la déclaration.

version : permets de définir la valeur de l'attribut de version qui sera rajouté à la déclaration (si l'attribut méthode est déclaré xml ou html).

Dans l'exemple suivant nous attendons un fichier de sortie compatible HTML donc non prefixé de la déclaration XML.

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


</xsl:stylesheet>

Comment inclure un fichier XSLT dans un autre ?

Pour inclure un fichier XSLT à un autre, il suffit d'ajouter après la balise output une (ou plusieurs) balise include dont l'attribut href sert à spécifier l'emplacement d'un autre fichier XSLT.

Dans l'exemple de code XSLT suivant nous incluons deux fichiers l'un functions_urls.xslt et l'autre function_images.xslt. Ce format est particulièrement adapté pour scinder d'un côté ses fonctions de mise en forme, et de l'autre le layout (par exemple). 

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
	<xsl:output method="html" omit-xml-declaration="yes" />
	<xsl:include href="functions_urls.xslt" />
	<xsl:include href="functions_images.xslt" />

</xsl:stylesheet>

Ces fichiers peuvent contenir des fragments de modèle. Nous y reviendrons un peu plus bas.

Comment créer un template racine en XSLT ?

Pour créer un template de base en XSLT, il suffit d'ajouter l'élément template et de lui assigner l'attribut match. Ce dernier sert à spécifier le nœud racine à partir duquel commencer à travailler. Dans la grande majorité des cas ce noeud sera root.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
	<xsl:output method="html" omit-xml-declaration="yes" />
	
	
	<xsl:include href="functions_urls.xslt" />
	<xsl:include href="functions_images.xslt" />
	
	<xsl:template match="root">
	
	</xsl:template>

</xsl:stylesheet>

Comment afficher des données dans un fichier XSLT ?

Pour la suite des exemples, nous utiliserons un fichier XML simple comportant une structure facilement compréhensible et représentative d'une structure relationnelle de base de données.  Ainsi, le point d'entrée sera un uid de joueur, qui nous permettra de retrouver les autres données.

<?xml version="1.0"?>

<root>
	<!--Représentation de la base de donnée "players"-->
	<datatable id="players">
		<datarow id="12" name="john" uid="527bd5b5d689e" culture="fr-FR" />
		<datarow id="32" name="pierre" uid="425bd8b5d624e" culture="fr-FR" />
		<datarow id="25" name="paul" uid="198bc5b3d689f" culture="en-GB" />
	</datatable>
	
	<!--Représentation de la base de donnée "characters"-->
	<datatable id="characters">
		<datarow id="50" player_id="12" life="100" last_update="11/18/2020" />
		<datarow id="25" player_id="12" life="25"  last_update="11/18/2020" />
		<datarow id="24" player_id="25" life="32"  last_update="11/18/2020" />
	</datatable>
	
	<!--Représentation de la base de données clé/valeur "kv_flags"-->
	<datatable id="kv_flags">
		<datarow key="fr-FR" path="fr.png"><![CDATA[<div>Mon contenu</div>]]></datarow>
		<datarow key="en-GB" path="uk.png"><![CDATA[<div>My content</div>]]></datarow>
	</datatable>
</root>

Comment s'utilise le XPATH ?

Avant d'aller plus loin dans notre exemple, un rapide rappel sur les bases du XPATH. Une requête (ou expression) XPATH basique se structure comme suit :

//racine/parent/enfant/petitenfant

// = sert à définir la racine du document XML.

//racine = sert à définir la racine et à cibler le nœud racine du document XML.

Chaque séparateur / supplémentaire définit un niveau de descendant suivant (enfant, petit enfant, etc.).

Il est possible de cibler un nœud par un attribut en utilisant la syntaxe [@nom_attribut='valeur']

//racine/parent/enfant[@nom='jean']/petitenfant

Ce système de sélection est cumulable.

//racine/parent/enfant[@nom='jean'][@age&gt ;=number(32)]/petitenfant

Les conditions supérieures et inférieures sont possibles, mais il peut nécessiter de réaliser un cast des données. Les opérateurs supérieurs/inférieurs doivent être écrit avec leur correspondant html (&gt;) et inférieur (&lt;)

Il est également possible de réaliser des sélections par index dans les nœuds

//racine/parent/enfant[1]/petitenfant[2]

Comment créer une variable en XSLT ?

Pour créer une variable en XSLT rien de compliquer. Une simple balise autofermante variable avec deux attributs

name : l'indentifiant unique de la variable.

select : le XPATH permettant de séléctionner le fragment de XML souhaité. Attention cependant, il est à noter que cette dernière se place dans le contexte où elle est créée.

<xsl:variable name="players" select="datatable[@id='players']" />

Dans l'exemple ci-après le XPATH réel de notre variable est en réalité //root/datatable[@id='players'] car exécuté en descendant du <xsl:template match="root">.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
	<xsl:output method="html" omit-xml-declaration="yes" />
	<xsl:include href="functions_urls.xslt" />
	<xsl:include href="functions_images.xslt" />
	
	<xsl:template match="root">
		<xsl:variable name="players" select="datatable[@id='players']" />
	</xsl:template>

</xsl:stylesheet>

Comment appeler une variable en XSLT ?

Pour utiliser une variable on préfixe sa clé (son attribut name) du symbole dollar $. Puis on l'utilise dans notre requête XPATH.

$players/datarow

Comment afficher le contenu d'une requête XPATH en XSLT ?

Pour afficher le contenu de séléction d'une requête XPATH il suffit d'utiliser l'élément value-of en précisant la requête XPATH souhaitée dans l'attribut select.

<xsl:value-of select="$players/datarow[1]/@name" />

Comment afficher le contenu d'un noeud contenant du CDATA ?

Pour afficher le contenu d'un noeud il suffit d'utiliser la fonction text().

<xsl:value-of select="$kv_flags/datarow[1]/text()" />

Comment afficher le contenu sans echappement des caractères < et > ?

Si votre contenu est déja fortement typé (du html par exemple) et que vous avez la certitude de la sécurité de ces données. Vous pouvez rajouter l'attribut disable-output-escaping attention, ceci vous expose à des vulnérabilités XSS. 

<xsl:value-of select="$kv_flags/datarow[1]/text()" disable-output-escaping="yes"/>

Comment réaliser une boucle en XSLT ?

Pour continuer dans l'explication des contextes relatifs propre au XPATH et en même temps en profiter pour présenter les boucles ainsi que fournir des explications sur la portée des variables, nous allons réaliser une boucle for-each qui va parcourir chaque nœud datarow. Et alimenter une variable pour aller ensuite chercher l'image du drapeau correspondant à la région de chaque joueur.

 

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

	<xsl:include href="functions_urls.xslt" />
	<xsl:include href="functions_images.xslt" />
	
	<xsl:template match="root">
		<xsl:variable name="players" select="datatable[@id='players']" />
        <xsl:variable name="kv_flags" select="datatable[@id='kv_flags']" />

        <ul>
            <xsl:for-each select="$players/datarow">
                <!-- Cible : $players/datarow/@culture -->
                <xsl:variable name="player_culture" select="@culture" />

                <li>
                    <!--Affiche l'image du drapeau par rapport à l'attribut culture-->
                    <img>
                        <xsl:attribute name="src"><xsl:value-of select="$kv_flags/datarow[@key='$player_culture']/@path" /></xsl:attribute>
                    </img>
                </li>

            </xsl:for-each>
        </ul>

	</xsl:template>


</xsl:stylesheet>

Ici la variable kv_flags est accessible dans la boucle for-each, alors que la variable player_culture est écrasée et générée à chaque nouvelle occurrence de la boucle. Une fois la boucle terminée, elle n'est plus accessible dans le flux d'exécution.

Comment connaître la position de l'index dans une boucle en XSLT ?

La fonction position() permet dans le contexte d'une itération de connaître la position de l'index.

<xsl:for-each select="$characters/datarow">
	<xsl:variable name="i" select="position()" />
</xsl:for-each>

Comment ajouter un attribut à un élement en XSLT ?

 Comme vue dans l'exemple ci-dessus il est parfaitement possible d'ajouter des attributs à des éléments grâce à l'élément attribute qui prend comme attribut name. Une seule exigence est requise par le compilateur, les attributs doivent arriver avant le contenu de la balise. Dans l'exemple suivant, la balise reçoit l'attribut avant son contenu "Accueil"

<a>
   <xsl:attribute name="href">/home</xsl:attribute>
   Accueil
</a>

Comment créer une condition if ou switch en XSLT ?

Pour le if pas de soucis, il existe, mais n'a pas de déclinaison else.

<xsl:if test="$player_culture = 'fr-FR'">Français</xsl:if>

Pour le switch il est présent sous la forme du choose qui lui inclut un otherwhise correspondant à un default. Et à un when pour chaque contrôle. Lequel attend un attribut test dont la valeur correspondra à l'expression conditionnelle à évaluer.

<xsl:choose>
	<xsl:when test="$player_culture = 'fr-FR'">Français</xsl:when>
	<xsl:when test="$player_culture = 'en-GB'">Anglais</xsl:when>
	<xsl:otherwise>
		Ni Anglais, ni Français
	</xsl:otherwise>
</xsl:choose>

 Comment créer modèle (ou bloc) en XSLT ?

Il convient d'utiliser la balise template à nouveau mais en lui ajoutant l'attribut name avec pour valeur la clé qui correspondra au nom à appeler. Pour créer des paramètres il suffit d'utiliser la balise param qui se comporte de la même manière qu'une variable.

<xsl:template name="le_nom_de_mon_template">
	<!--Paramètre obligatoire-->
	<xsl:param name="param_1" />

	<!--Paramètre optionnel-->
	<xsl:param name="param_2">Valeur par défaut</xsl:param>

	<div>
		<xsl:value-of select="$param_1">
	</div>
</xsl:template>

Comment appeler un modèle en XSLT ?

Pour appeler un sous template il faut utiliser l'élément call-template et lui assigner l'attribut name avec pour valeur le nom du modèle que nous souhaitons utiliser. Pour définir les paramètres, il faut utiliser l'élément with-param avec pour attribut name la clé du paramètre et pour l'attribut select la requête XPATH souhaitée.

<xsl:call-template name="le_nom_de_mon_template">
      <xsl:with-param name="param_1" select="$players/datarow/@name" />
</xsl:call-template>

Appliquer à notre exemple :

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
	<xsl:output method="html" omit-xml-declaration="yes" />
	
	
	<xsl:include href="functions_urls.xslt" />
	<xsl:include href="functions_images.xslt" />
	
	<!--Template appelant-->
	<xsl:template match="root">
		<xsl:variable name="players" select="datatable[@id='players']" />
        <xsl:variable name="kv_flags" select="datatable[@id='kv_flags']" />
        <xsl:variable name="characters" select="datatable[@id='characters']" />

        <ul>
            <xsl:for-each select="$players/datarow">

                <li>
                   <xsl:call-template name="show_player_flag">
                        <xsl:with-param name="player_culture" select="@culture" />
                        <xsl:with-param name="kv_flag" select="$kv_flag" />
                    </xsl:call-template>

					<!--Affiche l'indicateur d'admin-->
                    <xsl:if test="number(@admin)=1"><span>Admin</span></xsl:if>
					
					
                    <!--Affiche le nom du joueur-->
                    <div>
                        <xsl:value-of select="@name" />
                    </div>
					
					 <!-- Affiche l'état de santé du personnage -->
                    <xsl:for-each select="$characters/datarow[@player_id=$player_id]">
                        <span>
                            <xsl:choose>
                                <xsl:when test="number(life) &gt; 100">blessé</xsl:when>
                                <xsl:otherwise>
                                   en parfaite santé
                                </xsl:otherwise>
                            </xsl:choose>
                        </span>
                    </xsl:for-each>

                </li>

            </xsl:for-each>
        </ul>

	</xsl:template><!--Fin template appelant-->
    
    <!--Template de résolution des drapeaux -->
    <xsl:template name="show_player_flag">
        <xsl:param name="player_culture" />
        <xsl:param name="kv_flag" />

        <img>
            <xsl:attribute name="src"><xsl:value-of select="$kv_flags/datarow[@key='$player_culture']/@path" /></xsl:attribute>
        </img>
    </xsl:template><!--Fin Template de résolution des drapeaux -->


</xsl:stylesheet>