Xslt 如何在XSL:apply模板中使用XSL变量?

Xslt 如何在XSL:apply模板中使用XSL变量?,xslt,Xslt,我对xsl:apply模板有一个相当复杂的调用: <xsl:apply-templates select="columnval[@id and not(@id='_Name_') and not(@id='Group') and not(@id='_Co

我对xsl:apply模板有一个相当复杂的调用:

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

表达式在其他地方重用,例如:

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

我想以某种方式概括它,这样我就可以定义它一次并在其他地方重用它。但是,这似乎不起作用:

<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]"/>
@id和not(@id=''u Name')和not(@id='Group')和not(@id='u Count')
有没有更好/不同的方法?我只想在对xsl:apply模板的多个不同调用中重用xpath表达式(其中一些从不同的子级选择)

这将在客户端应用程序中使用,因此很遗憾,我不能使用任何扩展或切换到XSLT 2(


谢谢。

我想看看如何使用xslt的扩展。我认为在“标准”xslt中无法做到这一点


此扩展可以执行您想要的操作:

使用扩展exsl:nodeset,您可以创建一个命名模板,该模板接受nodeset$x并根据静态谓词返回过滤后的nodeset

您还可以在XSLT 2.0中定义函数。

如何:

<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>

例如,您可以将
$filter
设为参数,并从外部传入


您不能(正如您所注意到的)使用变量来存储XPath表达式。

您不能在XSLT(至少不是XSLT 1.0)中动态构造XPath。但是您可以使用模板模式轻松完成您尝试执行的操作:

<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>

...

XSLT 1.0和XSLT 2.0都不支持动态评估

一种方法是在XSLT 2.0或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>

然后您可以使用此功能

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

当然,您可以按照Robert Rossney的建议,在特定的匹配模式中指定测试,这可能是最好的方法

如果您需要动态定义要使用的过滤函数,一个强大的工具是库,它在XSLT中实现高阶函数(HOF)。HOF是接受其他函数作为参数并可以返回函数作为结果的函数


使用这种方法,您可以动态确定并将执行测试的函数作为参数传递给
my:test()

重构@Robert Rossney和@Tomalak

<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> 


Updated question-不幸的是,我们被XSLT 1.0卡住了。:(Updated question-我们不能使用扩展,因为我们依赖MSXML进行转换:(+1)这是一种有效的方法。唯一的缺点是过滤器是硬编码的,因此不是可变的。如果不需要可变过滤器,我会采用这种解决方案。模板模式对我来说毫无意义,直到我开始遇到像OP这样的问题。愚蠢的问题-为什么你可以用参数而不是变量来实现这一点?@Colen:变量(或参数)
$filter
存储字符串,而不是表达式。我选择了
作为分隔符。-)问得好。请参阅我的答案,了解两种可能的解决方案(XSLT 1.0和XSLT 2.0)的描述以及使用高阶函数的更强大解决方案的提示。由于属性是元素的按定义成员,因此可以用
*
:-)替换泛型
节点()