Accueil
 chercher             Plan du site             Info (English version) 
L'histoire de XML s'écrit en ce moment même. XMLfr vous aide à la suivre et à en dégager les tendances.Les listes de discussions XMLfr sont à votre disposition pour réagir sur nos articles ou simplement poser une question.Si vous ètes passionnée(e) par XML, pourquoi ne pas en faire votre métier ?XMLfr n'est heureusement pas le seul site où l'on parle de XML. Découvrez les autres grâce à XMLfr et à l'ODP.Les partenaires grâce auxquels XMLfr peut se développer.Pour tout savoir sur XMLfr.XMLfr sans fil, c'est possible !Pour ceux qui veulent vraiment en savoir plus sur XML.L'index du site.
 Si vous vous posez une question, vous n'êtes peut-être pas le premier...Les traductions en français des bibles XML.Ces articles sont des références dans leur domaine.Tout ce qu'il faut savoir pour démarrer sur un sujet XML...


Node sets et result tree fragments XSLT 1.0

Qu'est-ce qu'un "result tree fragment"? Qu'est-ce qu'un "node set"? Comment passer de l'un à l'autre?

Eric van der Vlist, Dyomedea (vdv@dyomedea.com).
mercredi 13 octobre 2004

Table des matières

Introduction

Source et résultat : deux types d'arbres très différents

Node sets et result tree fragments

Node sets

Result tree fragment

Peut-on éviter les result tree fragments?

Pour les littéraux

Pour des résultats intermédiaires

Variables conditionnelles

Conversion de result tree fragments en node sets

Et XSLT 2.0

Références

Introduction

Si vous vous posez cette question, c'est sans doute que vous avez été confronté à une erreur du type, "cannot convert to node-set" (XT), "java.lang.ClassCastException" (Xalan) ou "text copy failed" (libxslt).

Pour comprendre pourquoi vous obtenez cette erreur, il faut revenir sur la dissymétrie avec laquelle XSLT 1.0 traite les arbres source et résultat.

Source et résultat : deux types d'arbres très différents

XSLT 1.0 traite les arbres source et résultat de manière très différente.

L'arbre source, c'est à dire celui qui représente le document à transformer, est accessible en lecture seule et en accès aléatoire puisque l'on peut, grâce à XPath, accéder aux noeuds qui le composent dans l'ordre que l'on souhaite.

L'arbre résultat, c'est à dire celui qui représente le document résultant de la transformation, est au contraire accessible en écriture seule et en accès "séquentiel" puisqu'on ne peut insérer de noeud qu'à la suite de ceux qui ont déjà été insérés.

Node sets et result tree fragments

On retrouve cette distinction dans la manière dont on peut définir une variable ou un paramètre dans une transformation XSLT.

Node sets

Si j'écris :

<xsl:variable name="ma.variable" select="mon/chemin"/>

ou

<xsl:variable name="ma.variable" select="document('externe.xml')/
mon/chemin"/>

la variable "ma.variable" désigne un ensemble de noeuds présents dans un arbre source et il s'agit donc d'un "node set" (ensemble de noeuds).

L'accès à cette variable se fait donc sur le même mode (lecture seule, accès aléatoire) que l'accès à l'arbre source et ont peut accéder aux noeuds composant ce node set en utilisant des expressions XPath :

<xsl:value-of select="$ma.variable/@attribut"/>

ou

<xsl:apply-templates select="$ma.variable/autre/chemin"/>

Result tree fragment

Si j'écris:

<xsl:variable name="ma.variable">
  <racine attribut="valeur">
    <élément>valeur</élément>
  </racine>
</xsl:variable>

ou

<xsl:variable name="ma.variable">
  <xsl:apply-templates select="mon/chemin">
</xsl:variable>

la variable "ma.variable" contient au contraire un fragment d'arbre résultat (result tree fragment). Dans le premier cas, ce fragment est constitué de littéraux et dans le deuxième il est constitué dynamiquement et contient le résultat de l'application des templates.

Dans les deux cas, la variable "ma.variable" est traitée comme un fragment d'arbre résultat et elle est soumise à la même restriction : on ne peut pas accéder à son contenu au moyen de requêtes XPath et la seule opération que l'on peut faire sur ce fragment est de le recopier dans l'arbre résultat :

<xsl:copy-of select="$ma.variable"/>

Peut-on éviter les result tree fragments?

L'accès aux informations contenues dans une variable ou un paramètre de type result tree fragment étant bloqué, on aura intérêt lorsque c'est possible à éviter d'utiliser des result tree fragments.

Pour les littéraux

Lorsque la variable contient des littéraux, comme dans notre premier exemple :

<xsl:variable name="ma.variable">
  <racine attribut="valeur">
    <élément>valeur</élément>
  </racine>
</xsl:variable>

la fonction "document()" fournit un contournement qui permet de la définir sous forme de node set plutôt que de la définir sous forme de result tree fragment. Pour cela; on ajoutera un élément appartenant à un espace de noms autre que XSLT sous la racine de la transformation, par exemple :

<xsl:transform 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:var="http://ns.xmlfr.org/variable"
  version="1.0">

<var:valeur>
  <racine attribut="valeur">
    <élément>valeur</élément>
  </racine>
</var:valeur>

Il suffit ensuite de se rappeler que document("") (avec une chaîne vide en argument) désigne la transformation XSLT elle-même et nous pouvons définir notre variable comme :

<xsl:variable name="ma.variable" select="document('')/xsl:transfo
rm/var:valeur/racine"/>

Pour des résultats intermédiaires

Lorsqu'il s'agit de placer dans une variable des résultats intermédiaires comme dans notre deuxième exemple :

<xsl:variable name="ma.variable">
  <xsl:apply-templates select="mon/chemin">
</xsl:variable>

il n'y malheureusement pas de moyen d'éviter le passage par un result tree fragment et il faudra utiliser une extension pour convertir ce result tree fragment en node set.

Variables conditionnelles

Un autre cas dans lequel on ne peut pas éviter le passage par un result tree fragment est lorsque l'on doit affecter des valeurs conditionnelles à une variable.

Lorsque ces valeurs sont des chaînes de caractères, le problème ne se pose pas, mais lorsque ce sont des node set, on va être confronté à un nouveau problème.

Imaginons par exemple que nous devions absolument affecter à "ma.variable" l'élément "foo" dans un cas et l'élément "bar" dans les autres cas (ces éléments étant situés sous le noeud contexte).

Pour les raisons exposées dans la FAQ "Affectation conditionnelle de variable XSLT", si nous écrivons :

<xsl:choose>
  <xsl:when test="mon.test">
    <xsl:variable name="ma.variable" select="foo">
  </xsl:when>
  <xsl:otherwise>
    <xsl:variable name="ma.variable" select="bar">
  </xsl:otherwise>
</xsl:choose>

nos variables sont bien des node sets mais leur portée ne dépassant pas l'élément dans lequel elles sont définies elles sont absolument inutilisables.

Il faut donc écrire :

<xsl:variable name="ma.variable">
  <xsl:choose>
    <xsl:when test="mon.test">
      <xsl:copy-of select="foo"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy-of select="bar"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

ce qui résout le problème de la portée de la variable mais a l'effet de transformer notre variable en result tree fragment.

Conversion de result tree fragments en node sets

Cette conversion est tellement utile que tous les processeurs XSLT proposent des extensions permettant de la réaliser.

Ces extensions sont malheureusement incompatibles et il faut vous reporter à la documentation de l'implémentation que vous utilisez pour les utiliser.

C'est le cas pour XT et MSXML.

La plupart des autres processeurs (notamment libxslt, Saxon, Xalan et 4XSLT) ont implémenté la fonction node-set() de ESXLT ce qui permet d'écrire des transformations portables entre ces implémentations.

Si nous désirons convertir la variable définie dans notre premier exemple en node set pour accéder à son attribut à l'aide de cette fonction, nous pourrons écrire :

<xsl:transform   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 
  xmlns:exsl="http://exslt.org/common"
  extension-element-prefixes="exsl"
  version="1.0">
<xsl:variable name="ma.variable">
  <racine attribut="valeur">
    <élément>valeur</élément>
  </racine>
</xsl:variable> 
.../...
<xsl:value-of select="exsl:node-set($ma.variable)/racine/@attribu
t"/>

On accéderait de la même manière au contenu de résultats intermédiaires.

Et XSLT 2.0

Le problème ne se pose pas pour XSLT 2.0 qui ignore purement et simplement la différence entre ces deux types de fonctions.

Références

Copyright 2004, Eric van der Vlist


 

Mots clés.



L'histoire de XML s'écrit en ce moment même. XMLfr vous aide à la suivre et à en dégager les tendances.


Les documents publiés sur ce site le sont sous licence "Open Content"
Conception graphique
  l.henriot  

Conception, réalisation et hébergement
Questions ou commentaires
  redacteurs@xmlfr.org