web-dev-qa-db-fra.com

Dans XSLT, comment incrémenter une variable globale à partir d'une portée différente?

Je traite un fichier XML dans lequel je souhaite conserver le nombre de nœuds afin de pouvoir l'utiliser comme ID lorsque j'écris de nouveaux nœuds.

Pour le moment, j'ai une variable globale appelée "compteur". Je peux y accéder dans un modèle, mais je n'ai pas trouvé le moyen de le manipuler dans un modèle.

Voici une version condensée de mon fichier XSLT:

<xsl:variable name="counter" select="1" as="xs:integer"/>

<xsl:template match="/"> 
   <xsl:for-each select="section">
      <xsl:call-template name="section"></xsl:call-template>
   </xsl:for-each>
</xsl:template>

<xsl:template name="section">

   <!-- Increment 'counter' here -->

   <span class="title" id="title-{$counter}"><xsl:value-of select="title"/></span>
</xsl:template>

Des suggestions comment aller d'ici?

26
Marcel

D'autres ont déjà expliqué comment les variables sont immuables - qu'il n'y a pas d'instructions d'affectation dans XSLT (comme avec les langages de programmation purement fonctionnels en général).

J'ai une alternative aux solutions proposées jusqu'à présent. Cela évite de passer des paramètres (ce qui est prolixe et moche dans XSLT - même je l'admettrai).

Dans XPath, vous pouvez simplement compter le nombre d'éléments <section> qui précèdent l'élément en cours:

<xsl:template name="section">
  <span class="title" id="title-{1 + count(preceding-sibling::section)}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

(Remarque: la mise en forme de code d'espacement ne s'affiche pas dans votre résultat, car les nœuds de texte d'espacement ne sont automatiquement extraits de la feuille de style. Ne vous sentez donc pas obligé de mettre des instructions sur la même ligne.)

Un gros avantage de cette approche (par opposition à l'utilisation de position()) est qu'elle dépend uniquement du nœud actuel, pas de la liste des nœuds actuels. Si vous avez modifié votre traitement d'une manière ou d'une autre (par exemple, <xsl:for-each> a traité non seulement des sections mais également un autre élément), la valeur de position() ne correspond plus nécessairement à la position des éléments <section> dans votre document. Par contre, si vous utilisez count() comme ci-dessus, il correspondra toujours à la position de chaque élément <section>. Cette approche réduit le couplage avec d'autres parties de votre code, ce qui est généralement une très bonne chose.

Une alternative à count () serait d'utiliser l'instruction <xsl:number>. Son comportement par défaut numérotera tous les éléments portant le même nom au même niveau, ce qui se trouve être ce que vous voulez:

<xsl:template name="section">
  <xsl:variable name="count">
    <xsl:number/>
  </xsl:variable>
  <span class="title" id="title-{$count}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

C'est un compromis en termes de verbosité (nécessitant une déclaration de variable supplémentaire si vous souhaitez toujours utiliser les accolades du modèle de valeur d'attribut), mais très légèrement, car cela simplifie également considérablement votre expression XPath.

Il y a encore plus de place à l'amélioration. Bien que nous ayons supprimé la dépendance à la liste de nœuds actuels, nous dépendons toujours du nœud actuel. En soi, ce n’est pas une mauvaise chose, mais il n’est pas évident de regarder dans le modèle le noeud actuel. Tout ce que nous savons, c'est que le modèle s'appelle "section"; pour savoir avec certitude ce qui est en cours de traitement, nous devons chercher ailleurs dans notre code. Mais même cela n'a pas à être le cas.

Si jamais vous sentez amené à utiliser <xsl:for-each> et <xsl:call-template> ensemble (comme dans votre exemple), reculez et découvrez comment utiliser <xsl:apply-templates> à la place. 

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

<xsl:template match="section">
  <xsl:variable name="count">
    <xsl:number/>
  </xsl:variable>
  <span class="title" id="title-{$count}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

Non seulement cette approche est-elle moins explicite (<xsl:apply-templates/> remplace-t-elle <xsl:for-each> et <xsl:call-template/>), mais elle permet également de déterminer immédiatement le nœud actuel. Tout ce que vous avez à faire est de regarder l'attribut match et vous saurez instantanément que vous traitez un élément <section> et que les éléments <section> sont ce que vous comptez.

Pour une explication succincte du fonctionnement des règles de modèle (à savoir, les éléments <xsl:template> dotés d'un attribut match), voir "Comment fonctionne XSLT" .

44
Evan Lenz

Les variables XSLT ne peuvent pas être modifiées. Vous devrez transmettre la valeur d'un modèle à l'autre.

Si vous utilisez XSLT 2.0, vous pouvez avoir des paramètres et utiliser le tunneling pour propager la variable aux bons modèles.

Votre modèle ressemblera à quelque chose comme ceci:

<xsl:template match="a">
<xsl:param name="count" select="0">
  <xsl:apply-templates>
     <xsl:with-param select="$count+1"/>
  </xsl:apply-templates>
</xsl:template>

Examinez également l'utilisation de generate-id () si vous souhaitez créer des identifiants.

8
BeWarned

Les variables dans XSLT sont immuables, vous devez donc vous en préoccuper. Vous pouvez soit utiliser directement position():

<xsl:template match="/"> 
   <xsl:for-each select="section">
      <xsl:call-template name="section"/>
   </xsl:for-each>
</xsl:template>

<xsl:template name="section">
   <span class="title" id="title-{position()}"><xsl:value-of select="title"/></span>
</xsl:template>

Ou d'une manière plus orientée template:

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

<xsl:template match="section">
   <span class="title" id="title-{position()}"><xsl:value-of select="title"/></span>
</xsl:template>
6
jelovirt

les variables sont localisées et lues uniquement dans xslt.

2
Luixv

Selon votre processeur XSLT, vous pourrez peut-être introduire des fonctions de script dans votre XLST. Par exemple, la bibliothèque Microsoft XML prend en charge l’inclusion de javascript. Voir http://msdn.Microsoft.com/en-us/library/aa970889(VS.85).aspx pour un exemple. Cette tactique ne fonctionnera évidemment pas si vous prévoyez de déployer/exécuter XSLT sur les navigateurs clients publics; cela doit être fait par un processeur XSLT spécifique.

2

Vous pouvez utiliser la fonction position () pour faire ce que vous voulez. Cela ressemblerait à quelque chose comme ça.

<xsl:template match="/">
  <xsl:for-each select="section">
    <xsl:call-template name="section">
      <xsl:with-param name="counter" select="{position()}"/>
    </xsl:call-template>
  </xsl:for-each>
</xsl:template>

<xsl:template name="section">
  <xsl:param name="counter"/>
  <span class="title" id="title-{$counter}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>
1

Utilisez <xsl:variable name="RowNum" select="count(./preceding-sibling::*)" /> Et$ RowNumcomme valeur incrémentante.

Exemple: <xsl:template name="ME-homeTiles" match="Row[@Style='ME-homeTiles']" mode="itemstyle"> <xsl:variable name="RowNum" select="count(./preceding-sibling::*)" /> ...<a href="{$SafeLinkUrl}" class="tile{$RowNum}"><img ....></a>

Cela créera des classes pour le lien avec les valeurs tile1, tile2, tile3 etc ...

0
marika.daboja

Je n'ai pas essayé moi-même, mais vous pouvez essayer de passer un paramètre au modèle . Dans votre premier modèle, définissez le paramètre sur count () (ou current () peut-être?) Dans l'instruction for-each transmettez cette valeur à votre modèle "section".

Voici plus sur passer des paramètres aux templates

0
autonomatt