xslt
2010-10-07 9 views 9 likes 
9

J'ai un appel assez complexe pour xsl: apply-templates:Comment utiliser une variable XSL dans xsl: apply-templates?

<xsl:apply-templates select="columnval[@id 
             and not(@id='_Name_') 
             and not(@id='Group') 
             and not(@id='_Count_')]"/> 

L'expression est réutilisée dans d'autres endroits comme celui-ci:

<xsl:apply-templates select="someothernode[@id 
              and not(@id='_Name_') 
              and not(@id='Group') 
              and not(@id='_Count_')]"/> 

Je veux généraliser en quelque sorte, donc je peux définir une fois et réutiliser ailleurs. Toutefois, cela ne semble pas fonctionner:

<xsl:variable name="x">@id and not(@id='_Name_') and not(@id='Group') and not(@id='_Count_')</xsl:variable> 
<xsl:apply-templates select="columnval[$x]"/> 
<xsl:apply-templates select="someothernode[$x]"/> 

Y at-il une meilleure/différente façon de le faire? Tout ce que je veux, c'est réutiliser l'expression xpath dans plusieurs appels différents à xsl: apply-templates (dont certains sélectionnent des enfants différents).

Cela va être utilisé dans une application client, donc je ne peux pas utiliser d'extensions ou passer à XSLT 2 malheureusement. :(

Merci.

+0

Bonne question. Voir ma réponse pour une description de deux solutions possibles (XSLT 1.0 et XSLT 2.0) et un soupçon d'une solution plus puissante utilisant des fonctions d'ordre supérieur. –

Répondre

5

Vous ne pouvez pas construire XPath dynamiquement dans XSLT (au moins, pas XSLT 1.0). Mais vous pouvez facilement accomplir ce que vous essayez de faire en utilisant les modes de modèle:

<xsl:apply-templates select="columnval" mode="filter"/> 
<xsl:apply-template select="someothernode" mode="filter"/> 

... 

<!-- this guarantees that elements that don't match the filter don't get output --> 
<xsl:template match="*" mode="filter"/> 

<xsl:template match="*[@id and not(@id='_Name_') and not(@id='Group') and not(@id='_Count_')]" mode="filter"> 
    <xsl:apply-templates select="." mode="filtered"/> 
</xsl:template> 

<xsl:template match="columnval" mode="filtered"> 
    <!-- this will only be applied to the columnval elements that pass the filter --> 
</xsl:template> 

<xsl:template match="someothernode" mode="filtered"> 
    <!-- this will only be applied to the someothernode elements that pass the filter --> 
</xsl:template> 
+0

+ 1 Ceci est une approche efficace, seul inconvénient est que le filtre est codé en dur et donc pas variable.Si un filtre variable n'est pas nécessaire, je voudrais aller avec cette solution – Tomalak

+0

Les modes de modèle n'avaient aucun sens pour moi du moins jusqu'à ce que je commence à rencontrer des problèmes comme les OP –

1

Je prendrais un coup d'œil à l'aide d'une extension xslt. Je ne

pense pas que vous pouvez le faire dans xslt « standard ». Cette extension peut faire ce que vous voulez : http://www.exslt.org/dyn/functions/evaluate/index.html

+0

Question mise à jour - nous ne pouvons pas utiliser les extensions, puisque nous nous appuyons sur MSXML pour faire la transformation :( – Colen

+0

msxsl: node-set fonctionnera, alors –

1

Avec l'extension exsl: nodeset, vous pouvez créer un modèle nommé qui accepte un $ x nodeset et renvoie le filtre nodeset selon votre prédicats statique

vous pouvez également définir une fonction, dans XSLT 2.0.

+0

Question mise à jour - malheureusement, nous sommes coincés avec XSLT 1.0.: ( – Colen

1

Que diriez-vous:

<xsl:variable name="filter" select="_Name_|Group|_Count_" /> 

<xsl:apply-templates select="columnval" mode="filtered" /> 
<xsl:apply-templates select="someothernode" mode="filtered" /> 

<xsl:template match="someothernode|columnval" mode="filtered"> 
    <xsl:if test="not(contains(
    concat('|', $filter,'|'), 
    concat('|', @id,'|'), 
))"> 
    <!-- whatever --> 
    </xsl:if> 
</xsl:template> 

Vous pouvez faire $filter un param, et passer de l'extérieur par exemple. Ce que vous ne pouvez pas faire (comme vous l'avez remarqué) est d'utiliser des variables pour stocker des expressions XPath.

+0

Question stupide - pourquoi pouvez-vous le faire avec des paramètres, mais pas des variables? – Colen

+0

@Colen: La variable (ou param, d'ailleurs, "$ filter") stocke une chaîne, pas une expression. delimiter. ;-) – Tomalak

1

deux XSLT 1.0 et XSLT 2.0 ne prennent pas en charge l'évaluation dynamique.

Une façon de faire est d'utiliser <xsl:function> dans XSLT 2.0 ou <xsl:call-template> dans XSLT 1.0.

<xsl:function name="my:test" as="xs:boolean"> 
    <xsl:param name="pNode" as="element()"/> 

    <xsl:variable name="vid" select="$pNode/@id"/> 

    <xsl:sequence select= 
    "$vid and not($vid=('_Name_','Group','_Count_')"/> 
</xsl:function> 

alors vous pouvez utiliser cette fonction:

<xsl:apply-templates select="columnval[my:test(.)]"/> 

Certainement, vous pouvez spécifier le test dans les modèles de match spécifiques comme suggéré par Robert Rossney, et cela pourrait être la meilleure façon.

Dans le cas où vous devez dynamiquement définir quelle fonction filtrage à utiliser, un outil puissant est la bibliothèque FXSL, qui implémente ordre supérieur-fonctions (FOH) en XSLT. HOF sont des fonctions qui acceptent d'autres fonctions en tant que paramètres et peuvent renvoyer une fonction comme résultat.

En utilisant cette approche, vous déterminez dynamiquement et transmettez au paramètre my:test() en tant que paramètre une fonction qui effectue le test.

2

refactorisation Rossney et @Tomalak @ Robert

<xsl:apply-templates select="columnval" mode="filter"/> 
<xsl:apply-templates select="someothernode" mode="filter"/> 

<xsl:template match="*" mode="filter"> 
    <xsl:param name="pFilter" select="'_Name_|Group|_Count_'"/> 
    <xsl:apply-templates select="self::* 
           [not(contains( 
             concat('|',$pFilter,'|'), 
             concat('|',@id,'|'))) 
           and @id]"/> 
</xsl:template> 
+1

Puisque les attributs sont par définition des membres d'éléments, vous pouvez remplacer le 'node()' générique par '*'. :-) – Tomalak

Questions connexes