XSL:如何测试当前节点是否是另一个节点的后代

问题描述:

我是XSLT的新手,但需要将其用于目前使用的CMS。我已经提出了一个问题,但我会尝试描述它,而不会涉及底层CMS的太多信息。如果你需要更多的上下文来帮助我,我可以添加它。XSL:如何测试当前节点是否是另一个节点的后代

所以我想要做的就是测试如果我的XML的节点是特定节点的后代。

<xsl:if test="$currentNode::IsADescendantOf($someNode)"> 
Write this out. 
</xsl:if> 

任何想法的人?

感谢提前:)

您应该使用UNION操作和节点集的大小比较:

<xsl:if test="count($someNode|$currentNode/ancestor::*) = count($currentNode/ancestor::*)"> 
Write this out. 
</xsl:if> 

如果$ someNode$ currentNode的祖先,$ someNode | $ currentNode /祖先:: *将返回与$ currentNode/ancestor :: *相同的节点集(node-set没有任何双重)。

如果不是,由于联合,第一个节点集将比第二个节点集多一个节点。

+0

@Erlock:非常感谢您的帮助。工作过一种享受。 – 2009-12-13 23:38:48

查看 “祖先” 轴在XSL/XPath的参考

<xsl:if test="ancestor::*[. is $somenode]"> 

编辑:我跟你一起学习。看看这工作(我希望我有这个系统上可用我的XSL调试器:-)

+0

@Jim Garrison:感谢您的快速回复。这似乎是合理的,但我得到这个错误: System.Xml.Xsl.XslLoadException:期望一个节点测试,找到'$'。 – 2009-12-11 06:28:07

+0

你说得对,我的解决方案不完整。我没有使用XSL ref,但基本思想是您需要使用祖先轴来选择所有的祖先节点,然后根据节点相等性将其过滤为$ somenode。 – 2009-12-11 06:32:29

+0

非常感谢。现在我得到“is-same-node()'是一个未知的XSLT函数”我会继续讨论它,但是让我知道你是否有任何其他想法。 – 2009-12-11 06:42:09

后代检查

,最好的办法是使用后代:: *轴。

如果您有一段XML看起来像这样并且想要匹配来自第一个<alpha>节点的后代<a>节点。

XML

<root> 
<!-- check for descendants of the following alpha node--> 
<alpha> 
    <!-- match the following node--> 
    <a>...</a> 
    <b>...</b> 
    <c>...</c> 
</alpha> 
<alpha> 
    <a>...</a> 
    <c>...</c> 
</alpha> 
</root> 

XSLT

<xsl:if test="/root/alpha[1]/descendant::a[. is current()]"> 

祖先检查

这在大多数情况下,很可能工作。

<xsl:if test="ancestor::*[name() = $node]"> 

<xsl:variable name="node" select="name(//alpha)"/> 

或者你也可以使用产生-ID()如果你有多个节点以比较,需要确保它是一个节点的匹配,而不是名称匹配。

<xsl:variable name="node" select="generate-id(//alpha[3])"/> 
<xsl:if test="ancestor::*[generate-id(.) = $node]"> 

is的比较操作者可以比较祖先时,因为它需要的叶节点不IMO被使用,并且叶节点没有后代。

但我建议使用descendant :: *轴,因为它更具可读性和灵活性。

一种便携式(XPath 1.0和2.0)的解决办法是:

<xsl:if test=" 
    $currentNode/ancestor::*[generate-id() = generate-id($someNode)] 
"> 
Write this out. 
</xsl:if> 

这走到祖先轴,并检查在它的每一个元件。如果(且仅当)其中一个祖先的唯一ID与唯一ID为$someNode匹配,则生成的节点集不为空。

非空节点集计算为true,所以条件满足。

测试 - 找到所有<baz>这是<foo>后裔:

<xml> 
    <foo> 
    <bar> 
     <baz>Test 1</baz> 
    </bar> 
    </foo> 
    <baz>Test 2</baz> 
</xml> 

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 
    <xsl:template match="/"> 
    <xsl:variable name="someNode" select="//foo[1]" /> 

    <!-- test each <baz> node if it has <foo> as a parent --> 
    <xsl:for-each select="//baz"> 
     <xsl:if test=" 
     ancestor::*[generate-id() = generate-id($someNode)] 
     "> 
     <xsl:copy-of select="." /> 
     </xsl:if> 

    </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

结果

<baz>Test 1</baz> 

注意,如果你指的是实际 CURREN t节点,就像我在for-each中那样,不需要单独的$currentNode变量。默认情况下,所有XPath都与当前节点相关。

变体将是自上而下的方式。这是低效率的,但(可能是由几个数量级):

<xsl:if test=" 
    $someNode[//*[generate-id() = generate-id($currentNode)]] 
"> 
Write this out. 
</xsl:if> 

答案很简单,

所有你需要做的是:

<xsl:if test="count(ancestor::somenode)>0"> 

      .. 。然后添加其余的

的逻辑是,如果节点是somenode的后代,那么它将有一个或多个该节点。