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

Posted

技术标签:

【中文标题】XSLT:测试当前元素是不是匹配变量 xpath【英文标题】:XSLT: Test if current element matches variable xpathXSLT:测试当前元素是否匹配变量 xpath 【发布时间】:2021-09-02 16:30:21 【问题描述】:

我有一个动态 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。

【问题讨论】:

不确定在这种情况下“匹配”究竟是什么意思。你的字符串代表一个 relative 路径;因此,它只会在从div 的上下文中评估时匹配实际路径。我不明白样式表应该如何知道这一点。 为什么s4不会被&lt;xsl:variable name="xpathMatches" select="'s4|s2[@class=''class1'']|d3'"/&gt;中的路径或模式选中?正如前面正确评论的那样,您的相对路径仅在 div 元素的上下文中才有意义,但在这种情况下,应该像 s2 元素一样选择 s4 元素。 是的,s4 也应该被选中。我没有正确写出想要的输出。 【参考方案1】:

首先,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 表达式从上下文项中向下选择,因此它永远不会选择上下文项本身。我猜您真正想要的是将此表达式视为 XSLT 模式,并针对此模式测试上下文项。但是在 XSLT 3.0 中没有工具可以匹配作为字符串动态提供的模式。

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

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

【讨论】:

【参考方案2】:

如果您使用匹配 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>

【讨论】:

以上是关于XSLT:测试当前元素是不是匹配变量 xpath的主要内容,如果未能解决你的问题,请参考以下文章

优化 xslt xpath 匹配表达式

XSLT转换无法模板匹配

如何使用 XSLT/XPath 生成逗号分隔的列表?

selenium自动化测试之元素定位二(xpath定位)

XPath 1.0:使用当前节点的父节点的属性值来寻找另一个匹配的节点

XSLT/XPath 中的当前节点与上下文节点?