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
不会被<xsl:variable name="xpathMatches" select="'s4|s2[@class=''class1'']|d3'"/>
中的路径或模式选中?正如前面正确评论的那样,您的相对路径仅在 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=" "/>
</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=" "/>
</xsl:template>
【讨论】:
以上是关于XSLT:测试当前元素是不是匹配变量 xpath的主要内容,如果未能解决你的问题,请参考以下文章