Si vous avez déjà visité cette page, actualisez la (F5) pour avoir les dernières mises à jour.

Utiliser PHP pour transformer du XML à l'aide de XSL (deuxième partie)

Table des matières

Introduction

La première partie de ce document est une page qui présente XML, XSL, XSLT de manière succincte mais "auto-portante", en ne négligeant pas les espaces de nommage, et décrit la manière de s'en servir en PHP. Cette première partie élémentaire est plutôt centrée sur les notions de bases essentielles utiles, notamment, pour transformer un document XML en un document HTML comportant une mise en forme.

La présente page s'attache à des notions un peu plus avancées autorisant des manipulations plus complexes, principalement dans le cadre d'une transformation d'un document XML en un autre document XML.

Quelques notions plus avancées

XML et XSL : les axes

Une utilisation un peu avancée de XSL ne peut pas ignorer la notion d'axe. En fait, tous les nœuds d'un document XML ne sont pas de la même espèce. Ils sont divisés en ensembles qui s'appellent des axes. Ce nom vient sans doute du fait que ces axes contraignent les recherches select de nœuds dans une certaine direction.

D'après la recommandation "XML Path Language (XPath) 3.1" du W3C de décembre 2015 [1], il y a 13 axes, certains regardant vers l'avant et d'autres vers l'arrière :

Vers l'avant :

Vers l'arrière :

L'axe child est l'axe par défaut. En conséquence, pour sélectionner un nœud dans un autre axe, il faut le dire explicitement ; ainsi @*, une notation abrégée pour attribute::*, concorde avec tout attribut.

XSL : variables, paramètres et modèles de valeur

XSL : variables et paramètres

Parmi tous les éléments de XSLT [4], il faut réserver une place à part aux variables et paramètres en raison de leur importance en programmation, comme dans tout langage.

Il est possible de définir des variables et de leur affecter des valeurs. Seulement, XML oblige, la syntaxe est inhabituelle.

<xsl:variable name="maVariable">
   <ul>Cette liste de contacts donne:
   <li>le nom</li>
   <li>le prénom</li>
   <li>la profession et la spécialité en français</li>
   <li>la profession et la spécialité en anglais</li>
   <li>le pays de domiciliation en français</li>
   <li>le pays de domiciliation en anglais</li>
   </ul>
</xsl:variable>

L'instruction qui précède définit la variable $maVariable. Tout ce qui se trouve entre les deux balises <xsl:variable name="maVariable"> et </xsl:variable> est la valeur de la variable. Une telle variable peut être utilisée en lieu et place d'une expression XPath. Il est aussi possible d'affecter une valeur à une variable en utilisant l'attribut select, comme pour les paramètres.

En effet, il est aussi possible de définir des paramètres et de leur affecter des valeurs. La différence principale avec une variable est qu'une variable... n'est pas variable (une fois affectée [entre ses balises ou par select], sa valeur ne peut pas être changée) tandis qu'un paramètre reçoit une valeur par défaut mais peut recevoir une nouvelle valeur grâce à l'instruction xsl:with-param (voir exemple plus bas). La valeur du paramètre peut être transmise à un modèle en déclarant le paramètre au début du modèle par xsl:param (mécanisme similaire à celui qui permet de passer des valeurs "en paramètres" à une fonction appelée par un programme principal).

<xsl:param name="monParam" select="'valeur initiale'" />

L'instruction qui précède définit le paramètre $monParam et lui affecte la valeur par défaut 'valeur initiale' (guillemets simples à l'intérieur des guillemets doubles).

XSL : les modèles de valeur

Les variables et paramètres peuvent, entre autres choses, être utilisées dans le cadre des modèles de valeur. La valeur "chaîne de caractères" d'un nœud attribut (ou d'un nœud texte à partir de XSLT 2.0) peut, dans certaines circonstances, contenir une expression entre accolades. Ce sont des modèles de valeur d'attribut (ou de texte).

Un modèle de valeur est une chaine composée de parties variables (expression XPath entre accolades) ou fixes. Si une partie fixe doit contenir une accolade, elle doit être doublée (comme {{ ou }}, mécanisme d'échappement). Lors de l'utilisation, les parties variables sont calculées et transformées en chaînes puis toutes les parties sont concaténées. Le résultat de l'évaluation d'un modèle de valeur est appelé sa valeur effective. En XSLT 1.0, une seule partie est autorisée. Si plusieurs sont présentes, seule la première est prise en compte (restriction non appliquée, semble-t-il, par l'interpréteur de w3schools [5]).

Tous les attibuts ne peuvent pas recevoir des modèles de valeur. À l'exception de l'attribut "format" de xsl:result-document, les modèles de valeur ne sont pas permis pour :

XSL : observations sur quelques fonctions

Des mesures de bruit ont été faites dans une entreprise et
les résultats d'amplitude de pression acoustique (en mPa)
en fonction du lieu sont rassemblés dans le fichier XML suivant.

CODE XML SOURCE (fichier mesures.xml)
<?xml version="1.0" encoding="UTF-8"?>
<mesures>
  <point>
    <lieu>Atelier</lieu>
    <amplitude>600</amplitude>
  </point>
  <point>
    <lieu>Bureau</lieu>
    <amplitude>6</amplitude>
  </point>
  <point>
    <lieu>Vestiaire</lieu>
    <amplitude>60</amplitude>
  </point>
</mesures>
CODE XSL (fichier mesures.xsl)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:php="http://php.net/xsl">
<xsl:output method="html" encoding="UTF-8"/>
<xsl:template match="mesures">
<table>
<tr><th>Lieu</th><th>Niveau de pression (mPa)</th><th>Niveau de bruit (dB)</th> </tr>
<xsl:for-each select="point">
  <tr>
    <td><xsl:value-of select="lieu"/></td>
    <td><xsl:value-of select="amplitude"/></td>
    <td><xsl:call-template name="calcul">
        <xsl:with-param name="pression" select="amplitude"/>
        </xsl:call-template></td>
  </tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="calcul">
<xsl:param name="pression"/>
<xsl:param name="ratio" select="string(50*$pression)"/>
<xsl:value-of select="round(20*php:function('log10',$ratio))"/>
</xsl:template>
</xsl:stylesheet>
CODE PHP (fichier mesures.php)
<!DOCTYPE html>
<html lang="fr">
<head>
<title>PHP+XSLT-Exemple</title>
<style>
td { text-align: center ; }
</style>
</head>
<body>
<h1>Résultat des mesures de bruit</h1>

<?php
$feuilleDeStyle = new DOMDocument();
$feuilleDeStyle->load("mesures.xsl");

$xmlSource = new DOMDocument();
$xmlSource->load("mesures.xml");

$monProc = new XSLTProcessor();
$monProc->registerPHPFunctions();
$monProc->importStyleSheet($feuilleDeStyle);
$xmlResultat=$monProc->transformToXML($xmlSource);

echo $xmlResultat ;
?>

</body>
</html>
Le code PHP (à gauche) s'inscrit dans une page HTML5 et lance la transformation XSL.
Le code XSL (au-dessus) place les données dans une table et y ajoute le niveau de bruit en décibels.

CODE XML RÉSULTAT
<table xmlns:php="http://php.net/xsl">
<tr>
<th>Lieu</th>
<th>Niveau de pression (mPa)</th>
<th>Niveau de bruit (dB)</th>
</tr>
<tr>
<td>Atelier</td>
<td>600</td>
<td>90</td>
</tr>
<tr>
<td>Bureau</td>
<td>6</td>
<td>50</td>
</tr>
<tr>
<td>Vestiaire</td>
<td>60</td>
<td>70</td>
</tr>
</table>


Pour voir ce résultat dans le navigateur (nouvelle fenêtre), ouvrir la page mesures.php

XSL : instructions utiles

Les exemples de feuilles de style XSL montrés plus loin sont tous appliqués au même document XML reproduit ci-dessous (enregistré dans le fichier exemple.xml [afficher son code source pour voir le XML]). Il représente sommairement une liste bilingue de contacts. Certains nœuds sont dotés d'un attribut donnant la langue dans laquelle est exprimée la valeur du nœud. Aucun espace de nommage n'est invoqué.

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="exemple.xsl"?>
<contacts>
    <contact>Contact :
        <nom>Dupont</nom>
        <prenom>Joseph</prenom>
        <profession langue="fr">Ingénieur<specialiste>Mécanique</specialiste></profession>
        <profession langue="en">Engineer<specialiste>Mechanics</specialiste></profession>
        <domicile langue="fr">France</domicile>
        <domicile langue="en">France</domicile>
    </contact>
    <contact>Contact :
        <nom>Doe</nom>
        <prenom>John</prenom>
        <profession langue="fr">Avocat<specialiste>Affaires</specialiste></profession>
        <profession langue="en">Lawyer<specialiste>Business</specialiste></profession>
        <domicile langue="fr">États-Unis</domicile>
        <domicile langue="en">United States</domicile>
    </contact>
    <contact>Contact :
        <nom>Schumacher</nom>
        <prenom>Hans</prenom>
        <profession langue="fr">Cordonnier<specialiste>Chaussures</specialiste></profession>
        <profession langue="en">Shoemaker<specialiste>Shoes</specialiste></profession>
        <domicile langue="fr">Allemagne</domicile>
        <domicile langue="en">Germany</domicile>
    </contact>
</contacts>

XSL : <xsl:copy>…</xsl:copy> (instruction à deux balises, ouvrante et fermante)

L'instruction xsl:value-of, introduite dans la première partie, réalise une copie de la valeur d'un nœud c'est-à-dire de son contenu, à l'exclusion des balises xml qui le définissent. Cependant, lorsque la transformation fait passer d'un document source XML à un document résultat également en XML, il est parfois utile de copier intégralement le nœud - c'est ce que fait l'instruction xsl:copy.

ATTENTION : xsl:copy peut copier balises et valeurs mais NE COPIE PAS les attributs ni les enfants du nœud.

Le contenu de xsl:copy, placé entre ses deux balises, indique ce que l'instruction doit inscrire dans l'élément copié. La table suivante en donne quelques exemples.

Feuille de styleRésultatCommentaire
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" />

<xsl:template match="contact">
<xsl:copy></xsl:copy>
</xsl:template>

</xsl:stylesheet>
<contact/><contact/><contact/> Dans son premier parcours automatique de l'arbre source, lorsque l'interpréteur rencontre un nœud "contact", il applique le modèle fourni : il fait une copie de la structure <contact>…</contact> et la laisse vide puisque xsl:copy est vide.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" />

<xsl:template match="contact">
<xsl:copy><xsl:apply-templates/></xsl:copy>
</xsl:template>

</xsl:stylesheet>
<contact>Contact : Dupont Joseph IngénieurMécanique EngineerMechanics France France </contact>
<contact>Contact : Doe John AvocatAffaires LawyerBusiness États-Unis United States </contact>
<contact>Contact : Schumacher Hans CordonnierChaussures ShoemakerShoes Allemagne Germany </contact>
Dans son premier parcours automatique de l'arbre source, lorsque l'interpréteur rencontre un nœud "contact", il applique le modèle fourni : il fait une copie de la structure <contact>…</contact> et y place le résultat de <xsl:apply-templates/>, c'est-à-dire une copie de la valeur (sans balises donc) du nœud courant et de tous ses enfants.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" />

<xsl:template match="/">
<xsl:apply-templates select="//contact/*"/>
</xsl:template>

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

</xsl:stylesheet>
<nom>Dupont</nom><prenom>Joseph</prenom> <profession>Ingénieur<specialiste>Mécanique</specialiste></profession> <profession>Engineer<specialiste>Mechanics</specialiste></profession> <domicile>France</domicile><domicile>France</domicile><nom>Doe</nom> <prenom>John</prenom><profession>Avocat<specialiste>Affaires</specialiste> </profession><profession>Lawyer<specialiste>Business</specialiste></profession> <domicile>États-Unis</domicile><domicile>United States</domicile><nom>Schumacher</nom> <prenom>Hans</prenom><profession>Cordonnier<specialiste>Chaussures</specialiste> </profession><profession>Shoemaker<specialiste>Shoes</specialiste></profession> <domicile>Allemagne</domicile><domicile>Germany</domicile> Le premier modèle, concordant avec la racine du document source, empêche son parcours automatique mais lance une exploration de tous les enfants de nœuds "contact". Pour chacun de ces nœuds, l'interpréteur trouve un modèle concordant : le second modèle, par node(), s'accorde à tout nœud qui est un enfant (mais pas aux attributs, non considérés comme des enfants). Ce dernier relance une exploration de tous les enfants de nœuds dans le sous-arbre issu du nœud courant et, pour chacun d'eux, trouve un modèle concordant : lui-même. Cette utilisation d'un modèle par lui-même illustre la propriété de récursivité de XSLT. L'appel récursif du modèle continue jusqu'à ce que les feuilles de l'arbre soient atteintes. À chaque niveau, le modèle recopie l'élément correspondant.
Comme la récursion a été lancée sur les enfants de "contact" par le premier modèle, le résultat est la concaténation des sous-arbres correspondants et les balises "contacts" et "contact" n'apparaissent pas.
L'observation confirme aussi que les attributs "langue" n'ont pas été reproduits.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" />

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

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

</xsl:stylesheet>
Contact :
    <nom>Dupont</nom>Dupont
    <prenom>Joseph</prenom>Joseph
    <profession langue="fr">Ingénieur<specialiste>Mécanique</specialiste></profession>
Ingénieur<specialiste>Mécanique</specialiste>Mécanique
    <profession langue="en">Engineer<specialiste>Mechanics</specialiste> </profession>Engineer<specialiste>Mechanics</specialiste>Mechanics
    <domicile langue="fr">France</domicile>France
    <domicile langue="en">France</domicile>France
  Contact :
    <nom>Doe</nom>Doe
    <prenom>John</prenom>John
    <profession langue="fr">Avocat<specialiste>Affaires</specialiste> </profession>Avocat<specialiste>Affaires</specialiste>Affaires
    <profession langue="en">Lawyer<specialiste>Business</specialiste> </profession>Lawyer<specialiste>Business</specialiste>Business
    <domicile langue="fr">États-Unis</domicile>États-Unis
    <domicile langue="en">United States</domicile>United States
  Contact :
    <nom>Schumacher</nom>Schumacher
    <prenom>Hans</prenom>Hans
    <profession langue="fr">Cordonnier<specialiste>Chaussures</specialiste> </profession>Cordonnier<specialiste>Chaussures</specialiste>Chaussures
    <profession langue="en">Shoemaker<specialiste>Shoes</specialiste> </profession>Shoemaker<specialiste>Shoes</specialiste>Shoes
    <domicile langue="fr">Allemagne</domicile>Allemagne
    <domicile langue="en">Germany</domicile>Germany
Pour avoir les attributs (non sélectionnés par node()), il faut ajouter une sélection de tout nœud sur leur axe, soit @* en notation abrégée, ici dans la descendance de "contact".

Par ailleurs, dans cet exemple, dans le select du premier modèle, "*" est remplacé par "node()" (en rouge, à gauche). Cela provoque une duplication des valeurs des nœuds (premières occurrences en rouge, au milieu). Cela s'explique bien : node() concorde avec tous les nœuds, donc le second modèle est appelé une première fois pour le nœud élément complet, qu'il copie (avec son xml), puis, un niveau plus loin, il est appelé une seconde fois pour le nœud inclus dans l'élément et copie sa valeur, etc.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" />

<xsl:template match="/">
<xsl:apply-templates select="//contact//@*|//contact"/>
</xsl:template>

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

</xsl:stylesheet>
<contact>Contact :
    <nom>Dupont</nom>
    <prenom>Joseph</prenom>
    <profession langue="fr">Ingénieur<specialiste>Mécanique</specialiste></profession>
    <profession langue="en">Engineer<specialiste>Mechanics</specialiste></profession>
    <domicile langue="fr">France</domicile>
    <domicile langue="en">France</domicile>
</contact>
<contact>Contact :
    <nom>Doe</nom>
    <prenom>John</prenom>
    <profession langue="fr">Avocat<specialiste>Affaires</specialiste></profession>
    <profession langue="en">Lawyer<specialiste>Business</specialiste></profession>
    <domicile langue="fr">États-Unis</domicile>
    <domicile langue="en">United States</domicile>
</contact>
<contact>Contact :
    <nom>Schumacher</nom>
    <prenom>Hans</prenom>
    <profession langue="fr">Cordonnier<specialiste>Chaussures</specialiste></profession>
    <profession langue="en">Shoemaker<specialiste>Shoes</specialiste></profession>
    <domicile langue="fr">Allemagne</domicile>
    <domicile langue="en">Germany</domicile>
</contact>
Pour corriger le résultat ci-dessus non souhaité, il suffit, dans le premier modèle, de sélectionner (outre les attributs) les seuls nœuds "contact" et de laisser agir la récursivité pour la suite.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" />

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

</xsl:stylesheet> 
<?xml-stylesheet type="text/xsl" href="exemple.xsl">
<contacts>
  <contact>Contact :     <nom>Dupont</nom>
    <prenom>Joseph</prenom>
    <profession langue="fr">Ingénieur<specialiste>Mécanique</specialiste></profession>
    <profession langue="en">Engineer<specialiste>Mechanics</specialiste></profession>
    <domicile langue="fr">France</domicile>
    <domicile langue="en">France</domicile>
  </contact>
  <contact>Contact :     <nom>Doe</nom>
    <prenom>John</prenom>
    <profession langue="fr">Avocat<specialiste>Affaires</specialiste></profession>
    <profession langue="en">Lawyer<specialiste>Business</specialiste></profession>
    <domicile langue="fr">États-Unis</domicile>
    <domicile langue="en">United States</domicile>
  </contact>
  <contact>Contact :     <nom>Schumacher</nom>
    <prenom>Hans</prenom>
    <profession langue="fr">Cordonnier<specialiste>Chaussures</specialiste></profession>
    <profession langue="en">Shoemaker<specialiste>Shoes</specialiste></profession>
    <domicile langue="fr">Allemagne</domicile>
    <domicile langue="en">Germany</domicile>
  </contact>
</contacts>
L'application de la technique récursive du second modèle de l'exemple précédent à l'ensemble du document permet de construire en quelques lignes la transformation "identité", qui produit un document résultat identique au document source [3].
L'intérêt en est qu'il est parfois plus commode d'appliquer une transformation identité et d'en supprimer certains éléments non voulus comme le montre l'exemple 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="xml" encoding="UTF-8" omit-xml-declaration= "no" />

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

<xsl:template match="profession"/>

</xsl:stylesheet>
<?xml-stylesheet type="text/xsl" href="exemple.xsl">
<contacts>
  <contact>Contact :
    <nom>Dupont</nom>
    <prenom>Joseph</prenom>
    <domicile langue="fr">France</domicile>
    <domicile langue="en">France</domicile>
  </contact>
  <contact>Contact :
    <nom>Doe</nom>
    <prenom>John</prenom>
    <domicile langue="fr">États-Unis</domicile>
    <domicile langue="en">United States</domicile>
  </contact>
  <contact>Contact :
    <nom>Schumacher</nom>
    <prenom>Hans</prenom>
    <domicile langue="fr">Allemagne</domicile>
    <domicile langue="en">Germany</domicile>
  </contact>
</contacts>
Le premier modèle est celui qui produit la transformation identité. Lorsque l'interpréteur, dans son parcours lancé par ce premier modèle, arrive au nœud "profession", il trouve deux modèles qui concordent: lui-même et le second modèle. Cependant, lorsque ce type de situation se produit, il existe une règle de priorité pour choisir le modèle appliqué. Un modèle plus spécifique, plus précis, a toujours une priorité plus élevée qu'un modèle générique. Ici, "profession" est plus précis que "@*|node()" donc c'est le deuxième modèle qui est utilisé. Ce deuxième modèle ne fait rien donc l'élément "profession" est supprimé.

En conclusion xsl:copy est une instruction à fort potentiel.

XSL : <xsl:copy-of select="…" /> (instruction à une balise, autofermante)

À la différence de xsl:copy, cette instruction fait une copie intégrale du nœud sélectionné et de ses descendants, y compris les attributs et les espaces de nommage.

Feuille de styleRésultatCommentaire
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" />

<xsl:template match="contact">
<xsl:copy-of select="."/>
</xsl:template>

</xsl:stylesheet>
<contact>Contact :
<nom>Dupont</nom>
<prenom>Joseph</prenom>
<profession langue="fr">Ingénieur<specialiste>Mécanique</specialiste></profession>
<profession langue="en">Engineer<specialiste>Mechanics</specialiste></profession>
<domicile langue="fr">France</domicile>
<domicile langue="en">France</domicile>
</contact>
<contact>Contact :
<nom>Doe</nom>
<prenom>John</prenom>
<profession langue="fr">Avocat<specialiste>Affaires</specialiste></profession>
<profession langue="en">Lawyer<specialiste>Business</specialiste></profession>
<domicile langue="fr">États-Unis</domicile>
<domicile langue="en">United States</domicile>
</contact>
<contact>Contact :
<nom>Schumacher</nom>
<prenom>Hans</prenom>
<profession langue="fr">Cordonnier<specialiste>Chaussures</specialiste></profession>
<profession langue="en">Shoemaker<specialiste>Shoes</specialiste></profession>
<domicile langue="fr">Allemagne</domicile>
<domicile langue="en">Germany</domicile>
</contact>
C'est le même résultat que l'antépénultième de la table précédente.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" />

<xsl:variable name="maVariable">
   <ul>Cette liste de contacts donne:    <li>le nom</li>
   <li>le prénom</li>
   <li>la profession et la spécialité en français</li>
   <li>la profession et la spécialité en anglais</li>
   <li>le pays de domiciliation en français</li>
   <li>le pays de domiciliation en anglais</li>
   </ul>
</xsl:variable>

<xsl:template match="contacts">
<xsl:copy-of select="$maVariable"/>
<xsl:apply-templates/>
</xsl:template>

<xsl:template match="contact">
<xsl:copy-of select="."/>
</xsl:template>

</xsl:stylesheet>
<ul>Cette liste de contacts donne:
<li>le nom</li><li>le prénom</li><li>la profession et la spécialité en français</li> <li>la profession et la spécialité en anglais</li><li>le pays de domiciliation en français</li> <li>le pays de domiciliation en anglais</li></ul>
<contact>Contact :
<nom>Dupont</nom>
<prenom>Joseph</prenom>
<profession langue="fr">Ingénieur<specialiste>Mécanique</specialiste></profession>
<profession langue="en">Engineer<specialiste>Mechanics</specialiste></profession>
<domicile langue="fr">France</domicile>
<domicile langue="en">France</domicile>
</contact>
<contact>Contact :
<nom>Doe</nom>
<prenom>John</prenom>
<profession langue="fr">Avocat<specialiste>Affaires</specialiste></profession>
<profession langue="en">Lawyer<specialiste>Business</specialiste></profession>
<domicile langue="fr">États-Unis</domicile>
<domicile langue="en">United States</domicile>
</contact>
<contact>Contact :
<nom>Schumacher</nom>
<prenom>Hans</prenom>
<profession langue="fr">Cordonnier<specialiste>Chaussures</specialiste></profession>
<profession langue="en">Shoemaker<specialiste>Shoes</specialiste></profession>
<domicile langue="fr">Allemagne</domicile>
<domicile langue="en">Germany</domicile>
</contact>
La variable ayant été créée et initialisée, le premier modèle concorde avec le nœud contacts et copie le contenu de la variable puis lance une exploration à partir de ce nœud. Le deuxième modèle concorde avec contact et copie son contenu intégral chaque fois qu'un tel nœud est rencontré.

Cet exemple illustre une technique utile pour copier plusieurs fois le même contenu à plusieurs endroits du résultat car l'instruction xsl:copy-of utilisant la variable peut être répétée.

XSL : <xsl:number … /> (instruction à une balise, autofermante)

Cette instruction, "numérote", fournit un moyen simple de numéroter des nœuds du document résultat. Elle délivre un entier en tenant compte de la position du nœud courant dans le document source et permet de spécifier son format. Sa syntaxe est assez riche puisque sa forme complète est la suivante.

<xsl:number
count="expression"
level="single|multiple|any"




from="expression"
value="expression"
format="formatstring"

lang="languagecode"
letter-value="alphabetic|traditional"

grouping-separator="character"
grouping-size="number"
/>
Tous les attributs sont optionnels (valeur par défaut en gras).
count : expression XPath spécifiant les nœuds à prendre en compte dans le comptage (par défaut, le nœud courant)
level : indique quelle information indique le numéro ;
single => le rang du nœud indiqué par count parmi ses frères concordants avec count ;
multiple => une suite de nombres reflétant la position du nœud courant dans l'arborescence du document source
    par la numérotation des nœuds sélectionnés par count.
any => le rang des nœuds concordants, numérotés sans considération de niveau.
from : l'expression XPath indique où doit commencer la numérotation
value : l'expression remplace la valeur calculée par une valeur fournie par l'utilisateur
format : une chaîne de caractères spécifiant le format,
par exemple  '1', '01', 'i', 'I', 'a', 'A', 'A. ', …
lang : permet de tenir compte des particularités de certains langages
letter-value : alphabetic fait traiter les chiffres romains comme des lettres
(utile seulement dans certains langages)
grouping-separator : spécifie le caractère séparateur des tranches numériques
grouping-size : nombre de chiffres de chaque tranche numérique
 

La table suivante donne quelques exemples d'application.

Feuille de styleRésultatCommentaire
<?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" encoding="UTF-8" />

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

<xsl:template match="profession">
<xsl:number />
</xsl:template>

</xsl:stylesheet>
  Contact :
    Dupont
    Joseph
    1
    2
    France
    France
  
  Contact :
    Doe
    John
    1
    2
    États-Unis
    United States
  
  Contact :
    Schumacher
    Hans
    1
    2
    Allemagne
    Germany
Quand l'interpréteur rencontre un nœud profession, il le remplace par un numéro unique (single)
qui est son rang, parmi les nœuds frères concordants avec count, dans le sous-arbre issu de son parent.
<?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" encoding="UTF-8" />

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

<xsl:template match="profession">
  <xsl:number
    count="contact"
  />
</xsl:template>

</xsl:stylesheet>
Contact :
  Dupont
  Joseph
  1
  1
  France
  France

Contact :
  Doe
  John
  2
  2
  États-Unis
  United States

Contact :
  Schumacher
  Hans
  3
  3
  Allemagne
  Germany
Quand count est renseigné, lorsque l'interpréteur rencontre un nœud "profession", il cherche en remontant sur l'axe ascendant-or-self le premier nœud qui concorde avec count et l'instruction number renvoie alors le rang de ce nœud parmi les nœuds concordant avec count issus du même parent - c'est-à-dire ici le rang du nœud "contact" ascendant du nœud profession courant. Si aucune correspondance n'est trouvée, number renvoie une chaîne vide.
<?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" encoding="UTF-8" />

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

<xsl:template match="specialiste">
<xsl:number
   count="contact|profession"
   level="multiple"
/>
</xsl:template>

</xsl:stylesheet>
Contact :
Dupont
Joseph
Ingénieur1.1
Engineer1.2
France
France

Contact :
Doe
John
Avocat2.1
Lawyer2.2
États-Unis
United States

Contact :
Schumacher
Hans
Cordonnier3.1
Shoemaker3.2
Allemagne
Germany
Si level est fixé à multiple, number renvoie le rang de chaque ancêtre du nœud sélection concordant avec count parmi ses frères (également concordants avec count) et construit une suite de nombres reflétant le chemin d'accès au nœud courant.

Si level est any, alors un nombre unique est renvoyé qui est le rang du nœud courant dans l'ensemble des nœuds sans considération de niveau. Cependant, la prise en compte de cette option n'est pas encore généralisée à tous les navigateurs.
<?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" encoding="UTF-8" />

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

<xsl:template match="contact">
<xsl:number value="2016" format="1" grouping-size="3" grouping-separator=" "/>
<xsl:text> = </xsl:text>
<xsl:number value="2016" format="I"/>
</xsl:template>

</xsl:stylesheet>
2 016 = MMXVI
2 016 = MMXVI
2 016 = MMXVI
Au lieu d'utiliser le comptage automatique, il est aussi possible d'imposer la valeur du nombre par l'attribut value et de la formater.

Dans cet exemple, l'interpréteur remplace chaque occurrence d'un nœud "contact" par la chaîne spécifiée qui contient deux fois 2016, en chiffres arabes et romains.

XSL : <xsl:call-template name="…"></xsl:call-template> (instruction à une ou deux balise[s])

Cette instruction exécute un modèle spécifique, appelé par son nom. Ce dernier est défini par l'attribut name de la balise template du modèle.

Feuille de styleRésultatCommentaire
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" />

<xsl:template match="contact">
<xsl:call-template name="showSpec"/>
</xsl:template>

<xsl:template name="showSpec">
<xsl:text>Spécialité:</xsl:text>
<xsl:value-of select="./profession/specialiste"/>
</xsl:template>

</xsl:stylesheet>
Spécialité:Mécanique
Spécialité:Affaires
Spécialité:Chaussures
Quand l'interpréteur rencontre un nœud contact, il appelle, pour le nœud courant, le modèle nommé showSpec. Celui-ci produit du texte: "Spécialité:" concaténé avec la valeur du descendant specialiste du nœud 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" encoding="UTF-8" />

<xsl:template match="contact">
<xsl:call-template name="showSpec" />
</xsl:template>

<xsl:template name="showSpec">

<xsl:number format="1. "/>
<a id="{profession}">
<xsl:value-of select="profession/specialiste"/>
</a>

</xsl:template>

</xsl:stylesheet>
1. <a id="IngénieurMécanique">Mécanique</a>
2. <a id="AvocatAffaires">Affaires</a>
3. <a id="CordonnierChaussures">Chaussures</a>
Dans cet exemple, le mécanisme du modèle de valeur est utilisé pour renseigner l'attibut id de l'ancre avec le texte du nœud "profession".
xsl:number renvoie le rang du nœud "contact" en cours parmi sa fratrie.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" />

<xsl:template match="contact">
<xsl:call-template name="showSpec">
<xsl:with-param name="monFormat" select="'i. '" />
</xsl:call-template>
</xsl:template>

<xsl:template name="showSpec">
<xsl:param name="monFormat"/>
<xsl:variable name="maVariable" select="'- Juste un exemple - '"/>
<xsl:number format="{$monFormat}"/>
<xsl:text>Spécialité:</xsl:text>
<xsl:value-of select="./profession/specialiste"/>
<xsl:value-of select="$maVariable"/>
</xsl:template>

</xsl:stylesheet>
i. Spécialité:Mécanique- Juste un exemple -
ii. Spécialité:Affaires- Juste un exemple -
iii. Spécialité:Chaussures- Juste un exemple -
Avec la forme à deux balises de xsl:call-template, il est possible de passer un paramètre à un modèle. Ce paramètre est déclaré dans le modèle par xsl:param (pour recevoir sa valeur) et, dans le modèle appelant, il est transmis par xsl:with-param.
L'attribut select de cette dernière instruction donne une expression XPath pour affecter au paramètre une valeur par défaut - ici directement une chaîne de caractères 'i. ' qui spécifie le format numérique (noter les guillemets simples à l'intérieur des guillemets doubles).
Dans cet exemple, le mécanisme du modèle de valeur est utilisé pour récupérer la valeur du paramètre $monFormat.

La valeur fixe de la variable est commode pour utiliser plusieurs fois une valeur constante.

Conclusion

Le lecteur parvenu à ce stade non épuisé
... et qui brûle d'en savoir plus
... ... est maintenant en mesure de se débrouiller tout seul en étudiant les documents cités en référence de cette partie et de la première partie ;-).

Références


© Sellig Zed, septembre 2016. Texte et illustrations diffusés sous licence Creative Commons "Attribution - Pas d'utilisation commerciale" 4.0 International (cf. http://creativecommons.org/licenses/by-nc/4.0/).


logo html 5 Validé avec le vérificateur du W3C