XSLT:测试当前元素是否匹配变量 xpath

XSLT: Test if current element matches variable xpath

我有一个提供给模板的动态 xpath 字符串,我想测试当前元素是否与模板中的 xpath 匹配。

我试过使用 <xsl:evaluate/>,但我不确定它的具体使用方式,或者它是否适合这项工作。

XSLT:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:fn="http://www.w3.org/2005/xpath-functions"
        xmlns:functx="http://www.functx.com"
        version="2.0">
    <!-- HTML output -->
    <xsl:output
            method="text"
            encoding="UTF-8"
            omit-xml-declaration="yes"
            standalone="yes"
            indent="no"
            media-type="string"/>
    
    <xsl:template match="*">
        <!-- This xpathMatches variable will be dynamically generated -->
        <xsl:variable name="xpathMatches" select="'s4|s2[@class=''class1'']|d3'"/>

        <xsl:apply-templates mode="test">
            <xsl:with-param name="xpathMatches" select="$xpathMatches" />
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="*" mode="test">
        <xsl:param name="xpathMatches"/>

        <xsl:variable name="xpathEval">
            <xsl:evaluate xpath="$xpathMatches" context-item="."/>
        </xsl:variable>
        
        <!-- This doesn't work-->
        <xsl:if test="$xpathEval">
            <xsl:value-of select="name()"/>
        </xsl:if>
    </xsl:template>


</xsl:transform>

输入:

<div>
    <s1 />
    <s2 class="class1"/>
    <s4 class="class7"/>
</div>

期望的输出:

s2
s4

由于 s2 和 s4 与 xpath 匹配,因此应该只返回那些元素名称。 但目前测试对所有元素都返回 true。

首先,xsl:evaluate是3.0新增的。您的样式表指定 2.0。这不会使其失败,但会造成混淆。

XSLT 1.0 和 2.0 不提供评估动态构造为字符串的 XPath 表达式的标准方法,尽管一些处理器为此目的提供了扩展函数。

这里使用xsl:evaluate是正确的,错误的是你使用xsl:variable。没有as属性,xsl:variable构造文档节点,文档节点的有效布尔值始终为真。您需要做的唯一更改是将 as="node()*" 添加到 xsl:variable 声明中。

== 稍后 ==

虽然代码现在可以做一些事情,但它并没有按照您所描述的要求进行:“测试当前元素是否与模板中的 xpath 匹配”。您的 XPath 表达式从上下文项向下 select 离子,因此它永远不会 select 上下文项本身。我想您真正想要的是将此表达式视为 XSLT 模式,并根据此模式测试上下文项。但是 XSLT 3.0 中没有工具可以匹配作为字符串动态提供的模式。

您在这里可以做的是测试上下文项 C 是否有某个祖先 A,当用作评估表达式的上下文项时,其结果中包含 C。为此,将表达式 $xpathMatches:

换行
<xsl:evaluate xpath="'exists(ancestor-or-self::*!(' 
  || $xpathMatches ||
  ') intersect .)'"/>  

如果您在匹配 div 的模板中处理事情,您需要

  <xsl:template match="div">
    <xsl:variable name="xpathMatches" select="'s4|s2[@class=''class1'']|d3'"/>
    <xsl:variable name="matched-nodes" as="node()*">
      <xsl:evaluate context-item="." xpath="$xpathMatches"/>
    </xsl:variable>
    <xsl:value-of select="$matched-nodes/name()" separator="&#10;"/>
  </xsl:template>

请注意,xsl:evaluate 是一项可选功能,您会发现它在 Saxon 10 所有版本、Saxon 9.8 和 9.9 PE 和 EE、Saxon-JS 2 和 Altova XML 2017 R3 和以后如果回忆对我有用的话。

在有限的情况下,当您想要 运行 样式表并且可以在编译 XSLT 之前使用和设置静态参数时,使用影子属性和静态参数可能就足够了:

  <xsl:param name="xpathMatches" select="'s4|s2[@class=''class1'']|d3'" static="yes" as="xs:string"/>
  
  <xsl:template match="div">
    <xsl:value-of _select="({$xpathMatches})/name()" separator="&#10;"/>
  </xsl:template>