Le web de Dominique Guebey – Bazar informatique

Page web  : http://www.dg77.net/tekno/xpath.htm


   D o m i n i q u e   G u e b e y    J u n g l e      Bazar informatique

Memento XSLT + XPATH

Sommaire

XPATH est une syntaxe (et non pas un langage) pour localiser une portion d'un document XML.

Les exemples font souvent référence aux fichiers XML utilisés pour enregistrer ces pages, on trouvera une description dans la "Documentation technique du site" [http://www.dg77.net/tekno/sitedoc.htm#xml].

Expressions XPATH dans feuilles XSLT

Nom de l’élément parent

Soit un élément title dans un fichier Docbook. S’il est sous une "sect1" (section de niveau 1), on veut mettre un titre de niveau "h3" ; pour "sect2" on veut "h4", etc.

<xsl:template match="dbk:title">
    <xsl:choose>
        …
        <xsl:when test="local-name(parent::node()) = 'sect1'">
            <h3>
                <xsl:call-template name="tattrib"/>
                <xsl:apply-templates/>
            </h3>
        </xsl:when>
        <xsl:when test="local-name(parent::node()) = 'sect2'">
            <h4>
                <xsl:call-template name="tattrib"/>
                <xsl:apply-templates/>
            </h4>
        </xsl:when>
        …

Eléments précédents et suivants

Enchaînement conditionnel de page. Condition : l’attribut e doit être non vide (cf "Documentation technique du site" [http://www.dg77.net/tekno/sitedoc.htm#xml]). On met des liens vers la page précédente et/ou suivante si ces/cette page sont/est dans le même cas. Le noeud en cours est un élément page, on vérifie l’attribut "e" de l’élément page précédent ou suivant.

Amélioration du 16 dec 2004 : dans les attributs "title" et "alt", on intègre les titres des pages concernées qui sont dans l’élément /description/titre (variable $xtitre).

<xsl:if test="@e != ''">
  <xsl:if test="preceding-sibling::*[position()=1]/@e != ''">
    <div class="navg">
      <xsl:variable name="data" select="preceding-sibling::*[position()=1]/@nomfic"/>
      <xsl:variable name="xtitre" select="preceding-sibling::*[position()=1]/dg:description/dg:titre"/>
      <a>
        <xsl:attribute name="href">
          <xsl:value-of select="$vch"/>
          <xsl:value-of select="$data"/>
        </xsl:attribute>
        <xsl:attribute name="title">Retour/Back (<xsl:value-of select="$xtitre"/>)</xsl:attribute>
        <img>
          <xsl:attribute name="src">
            <xsl:value-of select="$vch"/>design/images/bulprev.png
          </xsl:attribute>
          <xsl:attribute name="alt">Retour/Back (<xsl:value-of select="$xtitre"/>)</xsl:attribute>
          <xsl:attribute name="height">22</xsl:attribute>
        </img>
      </a>
      </div>
  </xsl:if>
  <xsl:if test="following-sibling::*[position()=1]/@e != ''">
    <div class="navd">
      <xsl:variable name="data" select="following-sibling::*[position()=1]/@nomfic"/>
      <xsl:variable name="xtitre" select="following-sibling::*[position()=1]/dg:description/dg:titre"/>
      <a>
        <xsl:attribute name="href">
          <xsl:value-of select="$vch"/>
          <xsl:value-of select="$data"/>
        </xsl:attribute>
        <xsl:attribute name="title">
          Suite/Next (<xsl:value-of select="$xtitre"/>
        </xsl:attribute>
        <img>
          <xsl:attribute name="src"><xsl:value-of select="
            $vch"/>design/images/bulnext.png
          </xsl:attribute>
          <xsl:attribute name="alt">
            Suite/Next (<xsl:value-of select="$xtitre"/>)
          </xsl:attribute>
          <xsl:attribute name="height">22</xsl:attribute>
        </img>
      </a>
    </div>
  </xsl:if>
</xsl:if>

Données d’un autre élément

Suivant le même principe que précédemment, on utilise le titre d’une page dont le nom du fichier est indiqué dans un attribut "retour".

<xsl:if test="@retour != ''">
<xsl:variable name="ret" select="@retour"/>
<xsl:variable name="xtitre" select="preceding-sibling::*[@nomfic=$ret]/dg:description/dg:titre"/>
<div class="navg">
  <a>
    <xsl:attribute name="href">
        <xsl:value-of select="$vch"/><xsl:value-of select="@retour"/>
    </xsl:attribute>
    <xsl:attribute name="title">
        Debut/Start (<xsl:value-of select="$xtitre"/>)
    </xsl:attribute>
    <img>
      <xsl:attribute name="src"><xsl:value-of select="$vch"/>
        design/images/bulstart.png
      </xsl:attribute>
      <xsl:attribute name="alt">
        Debut/Start (<xsl:value-of select="$xtitre"/>)
      </xsl:attribute>
      <xsl:attribute name="width">44</xsl:attribute>
      <xsl:attribute name="height">22</xsl:attribute>
    </img>
  </a>
</div>
</xsl:if>

Exemples divers de traitements XSLT

Sélection d’un élément défini par variable.

Un élément au début du fichier XML en entrée :

<d:somm niv="h3"/>

Le “ template ” déclenché par la présence de d:somm :

<xsl:template match="d:somm">
<xsl:variable name="niv" select="@niv"/>
<ul>
<xsl:for-each select="..//*[name()=$niv]">
  <li>
    <a>
      <xsl:attribute name="href">
        <xsl:text>#</xsl:text>
        <xsl:value-of select="@id"/>
      </xsl:attribute>
      <xsl:value-of select="."/>
    </a>
  </li>
</ul>

On obtient ainsi la liste de tous les titres contenus dans des balises “h3”.

Test d’existence d’un élément et choix, traitement for each sur une suite d’éléments.

Si l’élément où on se trouve ne contient pas d’élément fils auteur, on va rechercher un autre élément auteur chez le "parent" (i.e. au niveau supérieur, voir ci-dessous la recherche avec select="../auteur"). Voir l’exemple de la "galerie d’images" dans la page de documentation du site..

  <xsl:choose>
      <!--Test existence de l’element "auteur"-->
      <xsl:when test="auteur">
            <xsl:for-each select="auteur">
                <!--test : on ne sort les informations que si l'element est non vide-->
                <xsl:if test=". != ''">
                <!--Remarque : dans la boucle for-each : on utilise alors "."-->
                    <p><small>Auteur : <xsl:apply-templates/></small></p>
                </xsl:if>
            </xsl:for-each>
      </xsl:when>
      <!--Cas de la non existence de l'element "auteur"-->
      <xsl:otherwise>
            <!--Dans ce cas on cherche la meme balise au niveau superieur-->
            <xsl:for-each select="../auteur">
                <p><small>Auteur : <xsl:apply-templates/></small></p>
            </xsl:for-each>
      </xsl:otherwise>
  </xsl:choose>

Génération d’un fichier TXT — URLLIST d’un site

But

Création d’un fichier urllist.txt pour l’indexation par l’annuaire/moteur de recherche Yahoo. Ce fichier contient simplement la liste des URL des pages qu’on veut faire indexer. Les URL sont trouvés dans les fichiers sources XML du site.

Source des données

Pour chaque page web au format XHTML, les balises se trouvent au sein d’une balise page. La structure des fichiers XML est la suivante (pour plus d’information cf la page documentation du site [http://www.dg77.net/tekno/sitedoc.htm]) :

<?xml version="1.0" encoding="iso-8859-1"?>
<www xmlns="http://ns.dg77.net/XML/">
…
<page nomfic="tekno/nomdelapage.htm" >
    <description>
…
    </description>
    <h2>Balises HTML</h2><
…
</page>
<page nomfic="tekno/suivante.htm">
…
</page>
…
</www>
Transformation XSLT

La tâche consiste à récupérer le contenu de l’attribut nomfic de la balise page et de le sortir dans le fichier urllist.txt.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="2.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:dg="http://ns.dg77.net/XML/"
>
<xsl:output method="text"
		indent="no"
		encoding="UTF-8"
		media-type="text/xml"
/>
<!-- nom fichier a sortir passe en parametre (ex : urllist.txt -->
<xsl:param name="sortie"/>

<xsl:strip-space elements="*" />

<xsl:template match="/">
    <xsl:result-document href="{$sortie}">
	    <xsl:apply-templates/>
    </xsl:result-document>
</xsl:template>

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

<xsl:template match="dg:page">
    <xsl:if test="@nomfic != ''">
        <xsl:text>http://mondomaine.fr/</xsl:text>
        <xsl:value-of select="@nomfic"/>
        <xsl:text><!-- saut de ligne -->
</xsl:text>
    </xsl:if>
</xsl:template>
</xsl:stylesheet>
Batch de lancement

Exemple, le fichier XSLT ci-dessus est nommé gen_urllist.xsl) :

@echo off
:: GENERATION XSLT utilisant SAXON
:: Se positionner a la racine du site
:: parametres :
:: 1 : nom du fichier XML sans l'extension
:: 2 : nom du fichier sorti
::
echo Fichier xml lu : sources\%1.xml  Fichier sorti : %2
java -jar saxon8.jar sources\%1.xml gen_urllist.xsl sortie=%2
:: Visualisation du resultat
type %2 | more
pause
:: ajout dans le fichier final
type %2 >> urllist.txt
Version améliorée

Dans la partie description, il est possible de spécifier une balise meta. Le traîtement ci-dessous génère la ligne seulement si la page n’a pas une balise meta du type name="robots" content="noindex". Dans cette version, le nom du fichier et son chemin d’accès son passés en paramètres aux templates suivants.

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="2.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:dg="http://ns.dg77.net/XML/"
>
<xsl:output method="text"
		indent="no"
		encoding="UTF-8"
		media-type="text/xml"
/>
<!-- nom fichier a sortir  -->
<xsl:param name="sortie"/>
<xsl:strip-space elements="*" />
<xsl:template match="/">
  <xsl:result-document href="{$sortie}">
	<xsl:apply-templates/>
  </xsl:result-document>
</xsl:template>

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

<xsl:template match="dg:page">
    <xsl:if test="@nomfic != ''">
        <xsl:apply-templates select="dg:description">
            <xsl:with-param name="nomfic"><xsl:value-of 
            select="@nomfic"/></xsl:with-param>
        </xsl:apply-templates>
    </xsl:if>
</xsl:template>

<xsl:template match="dg:description">
<xsl:param name="nomfic"/>
    <xsl:choose>
        <xsl:when test="dg:meta[@name='robots'] and 
         (dg:meta[@content='noindex'] or dg:meta[@content='noindex,nofollow'])"/>
        <xsl:otherwise><!-- Si pas de meta "noindex" : sortie -->
            <xsl:text>http://mondomaine.fr/</xsl:text>
            <xsl:value-of select="$nomfic"/><xsl:text><!-- saut de ligne -->
</xsl:text>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

Génération de pages de redirection

Le site déménage. Comme dans l’exemple précédent, on utilise les information de la balise page. Cette fois ci on crée un fichier (x)html pour chaque page. Le nom de l’emplacement de sortie est passé en paramètres.

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE xsl SYSTEM "listent.ent">
<!-- Generation de pages de redirection -->
<xsl:stylesheet version="2.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://ns.dg77.net/XML/"
    xmlns:d="http://ns.dg77.net/XML/d/"
    exclude-result-prefixes="dg d"
>

<xsl:output method="xml" 
indent="yes" 
encoding="iso-8859-1" 
media-type="text/xml" 
doctype-public="-//W3C//DTD XHTML 1.1//EN" 
doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" 
omit-xml-declaration="yes"/>

<xsl:param name="repsortie"/>

<xsl:strip-space elements="*" />

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

<xsl:template match="dg:www">
 <xsl:for-each select="dg:page">
  <xsl:if test="@nomfic != ''">
  <xsl:variable name="nomfic" select="@nomfic"/>
   <xsl:result-document href="../{$repsortie}/{$nomfic}">
    <html xml:lang="fr">
     <head>
      <meta http-equiv="Content-Language" content="fr"/>
      <meta content="application/xml+xhtml; charset=iso-8859-1" http-equiv="Content-Type" />
      <meta>
<xsl:attribute name="http-equiv">Refresh</xsl:attribute>
<xsl:attribute name="content">5; URL=www.nouveaudomaine.net/<xsl:value-of select="@nomfic"/></xsl:attribute>
  	  </meta>
      <title>Redirection</title>
     </head>
     <body>
      <h2>Page déplacée / <i xml:lang="en">Moved page</i></h2>
       <p>Le site a déménagé. Suivre le lien suivant 
                si la redirection ne se fait pas automatiquement.</p>
                <p xml:lang="en">The site has moved. Follow that following link 
                if you are not automatically redirected.</p>
<xsl:variable name="lien">http://www.nouveaudomaine.net/<xsl:value-of select="@nomfic"/></xsl:variable>
       <p><a href="{$lien}"><xsl:value-of select="$lien"/></a></p>
     </body>
    </html>
   </xsl:result-document>
  </xsl:if>
 </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Traîtement de chaînes de caractères

Chaînes de caractères : sous-chaînes et concaténation

Cet exemple crée un fichier WAP (.wml) à partir de chaque item d’un fichier RSS 2.0.

Soit un fichier de news où chaque info est identifiée par un nom dans l’élément guid, sous la forme XXXXX-9999, "9999" étant un numérotage séquentiel (0000, 0001, 0002, etc.).

…
    <item>
      <guid>frrgf-0475</guid>
      <title>samedi 5 mars 2005 CIRCUIT de l'EURE - BEZU-SAINT-ELOI - 
        4e édition 100 km Hommes interrompu</title>
      <description>1 Belloir Gilles, ULA Quimper-Cornoua. 03:52:28 40.000, 
2 Larochette Philippe, Entente Gd Lyon (ASUBron) 03:55:20 32.500, 
3 Thanron Bernard, L'Astragale 03:50:44 30.000…</description>
      <link dg:typ="url">http://www.csf-paris-colmar.org/grand-fond/eur05agc.htm</link>
    </item>
    <item>
      <guid>aurch-0472</guid>
      <title>Sidney 6 mars 2005 Championnats d'Australie Nathan Deakes 1h19'39, 
      Jane Saville 1h32'49</title>
      <description>20km M 1 Nathan Deakes 1h19'39, 2 Luke Adams 1:21.39, 
      3 Adam Rutter 1:24.46 mins 20km F 1 Jane Saville 1h32'49, 2 Cheryl Webb 1:35.14 minutes, 
      Victorian Simone Wolowiec 1:35.44</description>
      <link dg:typ="url">
         http://sports.groups.yahoo.com/group/racewalking/message/14023
      </link>
    </item>
…

Chaque "item" doit générer un fichier Wap. Le but est de donner un nom unique à chacun d’entre eux. On utilise le numéro inclu dans <guid>. substring-after permet de récupérer le nombre qui se trouve après (after) le caractère " - " Avec "concat", on constitue le nom du fichier à sortir : "F" suivi du numéro puis de l’extension ".wml".

<xsl:template match="rss:item">
        <xsl:variable name="data" select="substring-after(rss:guid, '-')"/>
	<xalan:write select="concat('F',$data,'.wml')">
		<wml><xsl:call-template name="sortie"/></wml>
	</xalan:write>
</xsl:template>

Génération des liens entre item : chaque page wap a un lien vers la précédente et la suivante (si elles existent), ainsi que vers la première et la dernière. Le noeud en cours est un élément item, on va ainsi lire l’élément guid de l’item précédent ou suivant. Noter qu’on utilise les caractères "<" et ">" pour afficher les liens, ce qui entraîne l’utilisation de l’attribut disable-output-escaping="yes" dans l’élément xsl:text. Il convient d’utiliser l’entité représentant l’esperluette (&) pour que les entités &#60; et &#62; (codes hexadecimaux équivalents à < et >) soient vraiment reproduites dans le fichier en sortie. &#160;, équivalent de &nbsp;, insère une espace.

…
<xsl:template name="sortie">
<card><xsl:attribute name="id"><xsl:value-of select="rss:guid"/></xsl:attribute>
  <xsl:attribute name="title"><xsl:value-of select="rss:guid"/></xsl:attribute>
  <xsl:variable name="pre" select="substring-after(preceding::rss:guid[position()=1], '-')"/>
  <xsl:variable name="sui" select="substring-after(following::rss:guid[position()=1], '-')"/>
  <xsl:variable name="deb" select="substring-after(/rss:rss/rss:channel/rss:item/rss:guid, '-')"/>
  <xsl:variable name="fin" select="substring-after(following::rss:guid[last()], '-')"/>
  <p align="center"><b>Marche News</b></p>
  <p>
    <!-- TITRE  -->
	<b><xsl:value-of select="rss:title"/></b>
    <!-- LIENS  -->
	<br/>
        <small><a><xsl:attribute name="href">index.wml</xsl:attribute>Retour</a></small>
        <xsl:if test="$pre != ''">
        <small><a><xsl:attribute name="href">F<xsl:value-of select="$deb"/>.wml</xsl:attribute>
          <xsl:text disable-output-escaping="yes">&amp;#60;&amp;#60;&amp;#160;</xsl:text>
        </a></small>
        </xsl:if>
	<xsl:if test="preceding::rss:guid[position()=1] != ''">
	  <xsl:text>  </xsl:text>
	  <small><a><xsl:attribute name="href">F<xsl:value-of select="$pre"/>.wml</xsl:attribute>
            <xsl:text disable-output-escaping="yes">&amp;#60;&amp;#160;</xsl:text>
          </a></small>
	</xsl:if>
	<xsl:if test="following::rss:guid[position()=1] != ''">
          <xsl:text>  </xsl:text>
	  <small><a><xsl:attribute name="href">F<xsl:value-of select="$sui"/>.wml</xsl:attribute>
            <xsl:text disable-output-escaping="yes">&amp;#62;&amp;#160;</xsl:text>
          </a></small>
	</xsl:if>
        <xsl:if test="$fin != $sui">
	  <xsl:text>  </xsl:text>
	  <small><a><xsl:attribute name="href">F<xsl:value-of select="$fin"/>.wml</xsl:attribute>
            <xsl:text disable-output-escaping="yes">&amp;#62;&amp;#62;</xsl:text>
          </a></small>
        </xsl:if>
…

Les données du fichier XML supra donneront un fichier WML (WAP comme suit :

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.3//EN" "http://www.wapforum.org/DTD/wml13.dtd">
<wml>
<card id="aurch-0472" title="aurch-0472">
<p align="center"><b>Marche News</b></p>
<p>
<b>Sidney 6 mars 2005 Championnats d'Australie 
Nathan Deakes 1h19'39, Jane Saville 1h32'49</b>
<br/>
<small><a href="index.wml">Retour</a></small>
<small><a href="F0485.wml">&#60;&#60;&#160;</a></small>
<small><a href="F0475.wml">&#60;&#160;</a></small>
<small><a href="F0443.wml">&#62;&#160;</a></small>
<small><a href="F0467.wml">&#62;&#62;&#160;</a></small>
<small>20km M 1 Nathan Deakes 1h19'39, 2 Luke Adams 1:21.39, 3 Adam Rutter 1:24.46 mins 
20km F 1 Jane Saville 1h32'49, 2 Cheryl Webb 1:35.14 minutes, 
Victorian Simone Wolowiec 1:35.44</small>
<br/>
<small><a href="#aurch-0472">Debut</a></small>
<small><u>
http://sports.groups.yahoo.com/group/racewalking/message/14023
</u></small>
</p>
</card>
</wml>

Affichage :

affichage WAP

Remplacement conversion suppression de caractères (translate replace)

La fonction replace change la suite de caractères recherchée par celle qui est indiquée en remplacement : replace($ma_chaine, 'quelque soit', 'quel que soit')

La fonction translate opère caractère par caractère. Exemple pour supprimer les caractères accentués et autres : translate($ma_chaine, 'éèçàùäëïöüâêîôû', 'eecauaeiouaeiou')

Pour remplacer œ par oe, il faudra utiliser replace

Les deux opérations font la même chose si on les utilise pour traîter un unique caractère.

Suppression dans une chaîne de caractères des éventuels espaces au début et à la fin (les espaces internes sont préservés) :

<xsl:variable name="zguid">
 <xsl:value-of select="replace(rss2:guid, '^\s|\s+$', '')" />
</xsl:variable>

Template XSLT récursif : génération du chemin de retour à la racine

Tous les liens internes du site utilisent des chemins relatifs. Par exemple ../design/dg77.css signifie qu’on remonte d’un niveau de répertoire (remontée à la racine), puis qu’on redescend dans le répertoire "design" pour y lire le fichier css (feuille de style). Le chemin de retour est conservé dans la variable $vch. Il est calculé par un "template" ad-hoc, qui utilise les propriétés de récursivité d’XSLT. Le procédé consiste, pour chaque nom de répertoire (chaîne de caractères suivie par un "/") à ajouter "../" dans $vch.

Source d’inspiration : voir le tutorial d’IBM/Chuck White "Analyze non-XML data with XSLT" [http://www-106.ibm.com/developerworks/edu/x-dw-xdataxslt-i.html] sur l’utilisation de XSLT et sa récursivité.


<!-- 
***************************************************
*** calcul du chemin de retour a la racine :
*** utilise le template tchemin
*************************************************** -->
<xsl:variable name="vch">
<xsl:call-template name="tchemin">
        <xsl:with-param name="chemin"> </xsl:with-param>
        <xsl:with-param name="chaine" select="@nomfic"/>
</xsl:call-template>
</xsl:variable>

…

<!-- 
**************************************************
*** calcul du chemin de retour 
**************************************************-->
<xsl:template name="tchemin">
        <xsl:param name="chemin" />
        <xsl:param name="chaine" />
        <xsl:variable name="data" select="substring-before($chaine,'/')"/>
        <xsl:choose>
            <xsl:when test="string-length($data)>0">
                <xsl:call-template name="tchemin">
                    <xsl:with-param name="chemin" select="concat($chemin,'../')"/>
                    <xsl:with-param name="chaine" select="substring-after($chaine,'/')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise><xsl:value-of select="$chemin"/></xsl:otherwise>
        </xsl:choose>
</xsl:template>

Autres formules XSLT avancées

document : utilisation d’un fichier externe

Exemple pour le "log" général du site, enregistré sous le format standard RSS2.0

<page nomfic="log.htm">
    <description>
        <cre>2002-07-12</cre>
        <upd>2004-10-08</upd>
        <titre>Log du site dg77.net</titre>
        <description>Historique des pages http://exe.dg77.net</description>
        <keywords></keywords>
	<style css=""></style>
        <docu>../archives/log.xml</docu>
        <lang></lang>
    </description>
    <h2 class="noprint"><a href="logarchive.htm">Voir les archives</a></h2>

</page>

Ci-dessous, extrait simplifié du fichier log.xml. Noter l’utilisation d’un namespace (cf la mention xmlns:dg) pour identifier des informations "maison" sans risque d’interférer avec la définition officielle de RSS2 (namespace désigné par la mention xmlns tout-court). Elles permettra en l’espèce de connaître les dates de création et dernière modification du fichier au sein de la balise <dg:description>.

<?xml version="1.0" encoding="iso-8859-1"?>
<rss version="2.0" xmlns="http://blogs.law.harvard.edu/tech/rss" 
    xmlns:dg="http://ns.dg77.net/XML/"
> 
        <dg:description> 
            <dg:cre>2002-07-12</dg:cre>
            <dg:upd>2004-10-25</dg:upd> 
        </dg:description> 
        <channel>
	        <title>dg77.net Log</title>

	        <link>http://exe.dg77.net/archives/log.xml</link>
	        <description>Log du site DG77.net</description>
	        <language>fr</language>
        	<item>
	        	<title>2004-10-25:00 : tekno/lynx navigateur, 
                                intégration des balises LINK
                        </title>
		        <link>http://exe.dg77.net/tekno/lynxlink.htm</link>
		        <description>Une annexe donne des exemples concernant 
                                l'utilisation des balises LINK par Lynx
                        </description>
	        </item>

…
        </channel>
</rss>

Utilisation de l’instruction XSLT "document". Les pages utilisant un fichier externe ont un lien pointant vers lui. Noter le test pour déterminer le type de fichier ce qui permet de choisir l’icone et le type mime à spécifier.

<xsl:choose>
    <xsl:when test="dg:description/dg:docu != ''">
        <xsl:variable name="fic" select="dg:description/dg:docu"/>
        <xsl:apply-templates select="document($fic)"/>
        <xsl:apply-templates />
        <!-- ********* lien vers le fichier externe d'origine ************** -->
        <xsl:choose>
                <!-- cherche le format de fichier pour choisir icone et type mime -->
                <xsl:when test="document($fic)/rss2:rss/@version ='2.0'">
                        <xsl:variable name="typmime">application/rss+xml</xsl:variable>
                        <xsl:variable name="imgsrc">
                                <xsl:value-of select="$vch"/>design/images/rss2.png
                        </xsl:variable>
                        <xsl:variable name="imgalt">RSS 2.0</xsl:variable>
                        <xsl:call-template name="xmlicone">
                                <xsl:with-param name="typmime" select="$typmime"/>
                                <xsl:with-param name="imgsrc" select="$imgsrc"/>
                                <xsl:with-param name="imgalt" select="$imgalt"/>
                        </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                        <xsl:variable name="typmime">text/xml</xsl:variable>
                        <xsl:variable name="imgsrc">
                                <xsl:value-of select="$vch"/>design/images/xml.png
                        </xsl:variable>
                        <xsl:variable name="imgalt">XML</xsl:variable>
                        <xsl:call-template name="xmlicone">
                                <xsl:with-param name="typmime" select="$typmime"/>
                                <xsl:with-param name="imgsrc" select="$imgsrc"/>
                                <xsl:with-param name="imgalt" select="$imgalt"/>
                        </xsl:call-template>
                </xsl:otherwise>
        </xsl:choose>
    </xsl:when>
    <xsl:otherwise><xsl:apply-templates /></xsl:otherwise>
</xsl:choose>

Quand un fichier externe est utilisé, il est logique d’utiliser les dates indiquées dans ce dernier, plutôt que celles du fichier xml appelant.

<!-- 
***************************************************
*** dates de creation et modification 
***   tenir compte eventuellemnt du fichier externe (xsl:document)
*************************************************** -->
<xsl:variable name="fic" select="dg:description/dg:docu"/>
<xsl:variable name="datcre">
    <xsl:choose>
        <xsl:when test="$fic != ''">
            <xsl:choose>
                <xsl:when test="document($fic)/rss2:rss !='' ">
                    <xsl:value-of select="document($fic)/rss2:rss/dg:description/dg:cre"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="document($fic)/dg:liens/dg:description/dg:cre"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="dg:description/dg:cre"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:variable>
…

xsl:key (1) : regroupement -avec tri- (Grouping) d’éléments en fonction d’un attribut

Utilisation de la méthode "münchienne" [http://www.jenitennison.com/xslt/grouping/muenchian.html] . Inspiré par David Carlyle

Soit le fichier répertoire téléphonique, où chaque entrée a un attribut "rubr". Par exemple : f=famille, w=travail, etc.

    …
    <entries>
        <entry id="xolm" tel="02 55 66 77 88" rubr="f">Michel Xolbec</entry>
        <entry id="scha" tel="01 55 77 88 99" rubr="w">Alfons Schfunz</entry>
        <entry id="perg" tel="01 12 45 87 59" rubr="w">Gherard Perez</entry>
    </entries>
    …

On veut lister ces éléments regroupés par code @rubr" et triés sur @id.

Solution :

<!-- En tete de fichier xsl, fonction "key" pour grouper suivant le code 
rubrique (methode "muenchienne")  -->
<xsl:key name="rubrik" match="dg:entry" use="@rubr" />
…
<xsl:template match="dg:entries">
    <!-- regroupement et tri par "rubrique" -->
    <xsl:for-each 
        select="dg:entry[generate-id()=generate-id(key('rubrik',string(@rubr)))]">
        <xsl:sort select="@rubr" />
        …
            <xsl:for-each select="key('rubrik',string(@rubr))">
            <!-- selection et tri des entrees pour une "rubrique" -->
                <xsl:sort select="@id" />
                …

xsl:key (2) : récupérer un texte en fonction d’un code

Pour chaque code "rubr" de l’exemple ci-dessus, une liste des libellés correspondants figure en fin de fichier.

    …
    <parm>
        <item rubr="f">Famille Annie</item>
        <item rubr="w">Travail</item>
        <item rubr="a">Amis</item>
        <item rubr="m">Marche</item>
        <item rubr="v">Voisins</item>
        <item rubr="e">Fournisseurs</item>
        <item rubr="z">Divers</item>
    </parm>

En tête de chaque groupe, on met un titre avec le libellé. Remarque : dans cet exemple, on a utilisé le même nom d’attribut (rubr) dans le fichier de données et dans la table mais il peut très bien en aller autrement.

…

<xsl:key name="nomencl" match="dg:item" use="@rubr" />
…
<h2>
        <xsl:value-of select="@rubr"/><xsl:text> - </xsl:text>
        <xsl:value-of select="key('nomencl',@rubr)"/></h2>
…

xsl:key (3) : chaînage d’un fichier externe

Voir un exemple dans la page de description du site, section Fichier externe de description/menu et xsl:key [http://www.dg77.net/tekno/sitedoc.htm#ficindex].

mod : utilisation de la fonction MODULO

Coloration alternée des "cellules" d’un tableau ;  [position() mod 2 = 1]  les positions impaires ont un reste egal a 1 quand ont les divise par 2

    …
<xsl:template match="d:lien[position() mod 2 = 1]">
    <tr>
        <td valign="top" class="bgjaun">
                …
        </td>
        <td valign="top">
            <xsl:apply-templates/>
        </td>
    </tr>
</xsl:template>
<!-- Si le precedent template ne s'est pas execute sur un element 
     "d:lien", alors c'est celui-ci qui sera traite -->
<xsl:template match="d:lien">
    <tr>
        <td valign="top">
            …
        </td>
        <td valign="top" class="bgjaun">
            <xsl:apply-templates/>
        </td>
    </tr>
</xsl:template>

Utilisation de MODULO pour ranger une série d’éléments dans un tableau, en prenant 5 éléments par ligne.

<xsl:template match="d:galerie">
…
  <table>
    <xsl:for-each select="d:galimage[(position()-1) mod 5 = 0]">
      <xsl:variable name="chem">
        <xsl:call-template name="xchemin">
          <xsl:with-param name="chemin"> </xsl:with-param>
          <xsl:with-param name="chaine" select="d:galfic"/>
        </xsl:call-template>
      </xsl:variable>
      <!-- Sortie de la premiere "cellule" (balise "td") en debut 
      de ligne (balise "tr") -->
      <tr>
        <td>
            <xsl:call-template name="soricone">
                <xsl:with-param name="vch" select="$vch"/>
                <xsl:with-param name="chem" select="$chem"/>
            </xsl:call-template>
        </td>
        <!-- Sortie pour les 4 elements suivants -->
        <xsl:for-each select="following-sibling::node()[position() < 5]">
          <td>
            <xsl:call-template name="soricone">
                <xsl:with-param name="vch" select="$vch"/>
                <xsl:with-param name="chem" select="$chem"/>
            </xsl:call-template>
          </td>
        </xsl:for-each>
      </tr>
    </xsl:for-each>
  </table>
</xsl:template>-->

Syntaxe XPATH résumée

cf XPATH par zvon.org.

Si le chemin commence par '/', alors il représente un chemin absolu vers l’élément requis.

/AAA
Sélectionne l’élément racine AAA
/AAA/CCC
Sélectionne tous les éléments CCC qui sont enfants de l’élément racine AAA
/AAA/DDD/BBB
Sélectionne tous les éléments BBB qui sont enfants de DDD, qui sont enfants de l’élément racine AAA

Si le chemin commence par '//', alors tous les éléments du document qui correspondent au critère qui suit sont sélectionnés.

//BBB
Sélectionne tous les éléments BBB
//DDD/BBB
Sélectionne tous les éléments BBB qui sont enfants de DDD

L’étoile * sélectionne tous les éléments localisés par ce qui la précède dans le chemin

/AAA/CCC/DDD/*
Sélectionne tous les éléments inclus dans les éléments /AAA/CCC/DDD
/*/*/*/BBB
Sélectionne tous les éléments BBB qui ont trois ancêtres
//*
Sélectionne tous les éléments

Une expression entre crochets peut spécifier plus précisément un élément. Un nombre entre crochets donne la position d’un élément dans le jeu sélectionné. La fonction last sélectionne le dernier élément du jeu

/AAA/BBB[1]
Sélectionne le premier élément BBB, fils de l’élément racine AAA
/AAA/BBB[last()]
Sélectionne le dernier élément BBB, fils de l’élément racine AAA

Les attributs sont spécifiés par le préfixe @.

//@id
Sélectionne tous les attributs id
//BBB[@id]
Sélectionne tous les éléments BBB qui ont un attribut id
//BBB[@name]
Sélectionne tous les éléments BBB qui ont un attribut name
//BBB[@*]
Sélectionne tous les éléments BBB qui ont un attribut
//BBB[not(@*)]
Sélectionne tous les éléments BBB qui n’ont pas d’attribut

Les valeurs d’attributs peuvent être utilisées comme critère de sélection. La fonction normalize-space supprime les espaces de début et de fin puis remplace les séquences d’espaces blancs par un seul espace.

//BBB[@id='b1']
Sélectionne tous les éléments BBB ayant un attribut id dont la valeur est b1
//BBB[@name='bbb']
Sélectionne tous les éléments BBB ayant un attribut name dont la valeur est bbb
//BBB[normalize-space(@name)='bbb']
Sélectionne tous les éléments BBB ayant un attribut name dont la valeur est bbb. Les espaces de début et de fin sont supprimés avant la comparaison

La fonction count() compte le nombre d’éléments sélectionnés.

//*[count(BBB)=2]
Sélectionne les éléments ayant deux enfants BBB
//*[count(*)=2]
Sélectionne les éléments ayant deux enfants
//*[count(*)=3]
Sélectionne les éléments ayant trois enfants

La fonction name() retourne le nom de l’élément, la fonction start-with retourne vrai si la chaîne du premier argument commence par celle du deuxième et la fonction contains retourne vrai si la chaîne du premier argument contient celle du deuxième

//*[name()='BBB']
Sélectionne tous les éléments qui ont le nom BBB, équivalent à //BBB
//*[starts-with(name(),'B')]
Sélectionne tous les éléments dont le nom commence par la lettre B
//*[contains(name(),'C')]
Sélectionne tous les éléments dont le nom contient la lettre C

La fonction string-length retourne le nombre de caractères dans une chaîne. Vous devez utiliser &lt; comme substitutif de < et &gt; comme substitutif de >

//*[string-length(name()) = 3]
Sélectionne tous les éléments qui ont un nom dont le nombre de caractères est exactement trois
//*[string-length(name()) < 3]
Sélectionne tous les éléments qui ont un nom de un ou deux caractères
//*[string-length(name()) > 3]
Sélectionne tous les éléments qui ont un nom dont le nombre de caractères est strictement supérieur à trois

Plusieurs chemins peuvent être combinés avec le séparateur |

//CCC | //BBB
Sélectionne tous les éléments CCC et BBB
/AAA/EEE | //BBB
Sélectionne tous les éléments BBB et EEE qui sont enfants de l’élément racine AAA
/AAA/EEE | //DDD/CCC | /AAA | //BBB
Le nombre de combinaisons n’est pas restreint

L’axe enfant contient les enfants du noeud contextuel. L’axe enfant est celui par défaut et il peut être omis

/AAA
Equivalent à /child::AAA
/child::AAA
Equivalent à /AAA
/AAA/BBB
Equivalent à /child::AAA/child::BBB
/child::AAA/child::BBB
Equivalent à /AAA/BBB
/child::AAA/BBB
Les deux possiblilités peuvent être combinées

L’axe descendant (descendant) contient les descendants du noeud contextuel ; un descendant est un enfant ou un petit enfant etc. Aussi, l’axe descendant ne contient jamais de noeud de type attribut ou des noms d’espace.

/descendant::*
Sélectionne tous les descendants de l’élément racine et donc tous les éléments
/AAA/BBB/descendant::*
Sélectionne tous les descendants de /AAA/BBB
//CCC/descendant::*
Sélectionne tous les éléments qui ont CCC comme ancêtre
//CCC/descendant::DDD
Sélectionne les éléments DDD qui ont CCC comme ancêtre

L’axe "parent" contient le parent du noeud contextuel s’il en a un

//DDD/parent::*
Sélectionne tous les parents de l’élément DDD

L’axe ancêtre (ancestor) contient les ancêtres du noeud contextuel ; cela comprend son parent et les parents des parents etc. Aussi, cet axe contient toujours le noeud racine, sauf si le noeud contextuel est lui-même la racine.

/AAA/BBB/DDD/CCC/EEE/ancestor::*
Sélectionne tous les éléments donnés dans ce chemin absolu
//FFF/ancestor::*
Sélectionne tous les ancêtres de l’élément FFF

L’axe 'following-sibling' contient tous les noeuds frères qui suivent le noeud contextuel.

/AAA/BBB/following-sibling::*
Sélectionne tous les noeuds frères suivants de /AAA/BBB
//CCC/following-sibling::*
Sélectionne tous les noeuds frères suivants de l’élément CCC

L’axe 'preceding-sibling' contient tous les frères prédécesseurs du noeud contextuel ; si le noeud contextuel est un attribut ou un espace de noms, la cible précédente est vide.

/AAA/XXX/preceding-sibling::*
Sélectionne tous les noeuds frères prédécésseurs de /AAA/XXX
//CCC/preceding-sibling::*
Sélectionne tous les noeuds frères prédécésseurs de l’élément CCC

L’axe suivant (following) contient tous les noeuds du même document que le noeud contextuel qui sont après le noeud contextuel dans l’ordre du document, à l’exclusion de tout descendant, des attributs et des espaces de noms.

/AAA/XXX/following::*
Sélectionne tous les noeuds qui suivent /AAA/XXX dans le document et qui ne sont pas des descendants de cet élément
//ZZZ/following::*
Sélectionne tous les noeuds qui suivent l’élément ZZZ dans le document et qui ne sont pas des descendants de cet élément

L’axe cible précédente (preceding) contient tous les prédécesseurs du noeud contextuel ; si le noeud contextuel est un attribut ou un espace de noms, la cible précédente est vide.

/AAA/XXX/preceding::*
Sélectionne tous les noeuds qui précèdent le noeud /AAA/XXX dans le document qui ne sont pas ses parents
//GGG/preceding::*
Sélectionne tous les noeuds qui précèdent les éléments GGG dans le document qui ne sont pas ses parents

L’axe "descendant-or-self" contient le noeud contextuel et ses descendants

/AAA/XXX/descendant-or-self::*
Sélectionne le noeud /AAA/XXX et tous ses descendants
//CCC/descendant-or-self::*
Sélectionne tous les éléments CCC et leurs descendants

L’axe ancestor-or-self contient le noeud contextuel et ses ancêtres ; ainsi l’axe ancestor-or-self contient toujours le noeud racine

/AAA/XXX/DDD/EEE/ancestor-or-self::*
Sélectionne le noeud /AAA/XXX/DDD/EEE et tous ses ancêtres
//GGG/ancestor-or-self::*
Sélectionne tous les éléments GGG et tous leurs ancêtres

Les axes ancestor, descendant, following, preceding et self partitionnent un document (ignorant les attributs et les noeuds d’espace de nom) : il ne se chevauchent pas et ensemble ils contiennent tous les noeuds d’un document

//GGG/ancestor::*
Sélectionne tous les ancêtres des éléments GGG
//GGG/descendant::*
Sélectionne tous les descendants des éléments GGG
//GGG/following::*
Sélectionne tous les noeuds qui suivent les éléments GGG et qui ne sont pas des descendants de GGG
//GGG/preceding::*
Sélectionne tous les noeuds qui précèdent les éléments GGG et qui ne sont pas des ancêtres de GGG
//GGG/self::*
Sélectionne tous les éléments GGG
//GGG/ancestor::* | //GGG/descendant::* | //GGG/following::* | //GGG/preceding::* | //GGG/self::*
Sélectionne tout

L’opérateur div réalise une division à virgule flottante, l’opérateur mod retourne le reste d’une division. La fonction floor() retourne le plus grand nombre (le plus près de l’infini positif) qui n’est pas plus grand que l’argument et qui est un entier. La fonction ceiling() retourne le plus petit nombre (le plus près de l’infini négatif) qui n’est pas plus petit que l’argument et qui est un entier

//BBB[position() mod 2 = 0 ]
Sélectionne tous les éléments BBB dont la position dans l’arbre est un multiple de 2
//BBB[ position() = floor(last() div 2 + 0.5) or position() = ceiling(last() div 2 + 0.5) ]
Sélectionne le ou les éléments BBB qui se trouvent au milieu de tous les éléments BBB