获取当前节点的所有祖先节点

5
我希望你能够翻译当前节点的所有祖先节点:

XML:

<root>
   <item title="a">
       <item title="b">               
           <item title="c"></item> <!--CURRENT-->
           <item title="d"></item>                 
        </item> 
       <item title="x">             
           <item title="y"></item> 
           <item title="z"></item>  
       </item>            
   </item> 
</root>

结果:

<item title="a">...</item>
<item title="b">...</item>

编辑: 带有轴祖先的答案是可以的。 我的问题在XSLT中

XSLT:

<xsl:variable name="curr" select="//item[@title = 'c']"></xsl:variable>
<xsl:variable name="test" select="$curr/ancestor::item"></xsl:variable>

<xsl:for-each select="$test/item">
<xsl:value-of select="@title"></xsl:value-of>
</xsl:for-each>

返回:

bcdx

编辑2: 针对迪米特和所有遇到类似问题的人

我提出的问题的所有答案都很好。

只有 XSLT (up) 给我返回了一个奇怪的结果,@Mads Hansen 纠正了我。

最终可行的示例:

XML:

<?xml version="1.0" encoding="utf-8"?>
<root>
   <item title="a">
       <item title="b">               
           <item title="c"></item> 
           <item title="d"></item>                 
        </item> 
       <item title="x">             
           <item title="y"></item> 
           <item title="z"></item>  
       </item>            
   </item> 
</root>

XSLT:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:variable name="curr" select="//item[@title = 'c']"></xsl:variable>
        <xsl:variable name="test" select="$curr/ancestor::item"></xsl:variable>

        <xsl:for-each select="$test">
            <xsl:value-of select="@title"></xsl:value-of>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

返回:

ab

1
那么,XSLT代码有什么问题呢?我看到一个主要的问题——它不是语法上有效的——这个代码中甚至没有一个模板。因此,无法给出任何有意义的答案。请编辑问题并提供一个完整(但尽可能小)的转换,以便每个人都可以执行,从而可以重现问题。另外,请提供确切的预期结果以及为什么当前获得的结果是一个问题。 - Dimitre Novatchev
4个回答

5

祝贺Adam很快地给出了第一个答案。

补充一些细节:

您列出的预期结果与您的话不符。根元素也是祖先节点,文档也是祖先节点。

ancestor::node()

...将按照以下顺序返回一个序列:

  1. item[@title='b']
  2. item[@title='a']
  3. root元素(也称为文档元素)
  4. 根节点 /

要获取您列出的特定结果,您需要:

ancestor::item/.

/的效果是将顺序更改回正向文档顺序。ancestor::的本地顺序是反向文档顺序。


更新:评论中提到的要点的说明。

这个样式表(带有OP的输入)...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="/">
  <xsl:for-each select="//item[@title='c']">
    <xsl:value-of select="ancestor::item[1]/@title" />
    <xsl:value-of select="ancestor::item[2]/@title" />
  </xsl:for-each>  
</xsl:template>         
</xsl:stylesheet>

...将输出“ba”,说明ancestor::确实是一个反向轴。然而,这个样式表...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="/">
  <xsl:for-each select="//item[@title='c']">
    <xsl:value-of select="(ancestor::item/@title)[1]" />
    <xsl:value-of select="(ancestor::item/@title)[2]" />
  </xsl:for-each>  
</xsl:template>

</xsl:stylesheet>

...具有相反的结果'ab'。这很有启示性,因为它显示在XSLT 1.0中(而不是XSLT 2.0),括号会消除反向性质,并将其变成一个文档排序节点集。

OP询问了类似于...的转换。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="/">
  <xsl:for-each select="//item[@title='c']">
    <xsl:for-each select="ancestor::item">
      <xsl:value-of select="@title" />
    </xsl:for-each>
  </xsl:for-each>  
</xsl:template>

</xsl:stylesheet>

这个例子返回“ab”(在XSLT 2.0中会返回“ba”)。为什么?因为在XSLT 1.0中,xsl:for-each指令忽略了轴的反向性并按文档顺序处理(除非xsl:sort指令另有规定)。


我使用 / 和不使用 / 得到了相同的结果。"<xsl:for-each select="//item[@title = 'c']/ancestor::item">" 和 "<xsl:for-each select="//item[@title = 'c']/ancestor::item/.">" 是一样的。结果是:ab。 - Moriarty
在XPATH 1.0或2.0中,ancestor::确实是一个反向轴(参见http://www.w3.org/TR/xpath/第2.4节;和http://www.w3.org/TR/xpath20/第3.2.1.1节),但是你在XSLT中实际使用它的方式可能会改变顺序 - 并且行为取决于XSLT版本。 - Sean B. Durkin
在XSLT 1.0中,xsl:for-each指令将忽略xpath表达式中的顺序(除非应用了排序)。但是在XSLT 2.0中,内在顺序被保留。 - Sean B. Durkin
为了理解我的意思,尝试使用位置谓词。即使在 XSLT 1.0 中使用 xsl:for-each,你也会得到相反的顺序。 - Sean B. Durkin
我将发布一小段XSLT 1.0来说明。 - Sean B. Durkin
我之前评论中的语句“即使在使用xsl:for-each的XSLT 1.0中,您也会得到相反的顺序”表达得非常糟糕。请忽略那个。 - Sean B. Durkin

3

我想获取当前节点的所有祖先节点

显然,指定的节点并不是“绿色”节点的所有祖先节点。

要选择所有祖先,请使用:

ancestor::node()

这选择初始上下文节点(或当前节点)的所有祖先节点,包括顶级元素及其父元素——根节点/,它是一个节点,但不是一个元素。 要选择所有元素祖先,请使用:
ancestor::*

这与先前的表达式类似,但不选择 根节点 (/),因为它不是一个元素。 要选择所有名为item的祖先,请使用::
ancestor::item

注意:所有上述表达式都假定(//item[@title='c'])[1]是初始上下文节点(当前节点)。如果这个假设不正确,则必须在每个表达式前面添加(//item[@title='c'])[1]/

注意2:我强烈建议学习XPath使用像the XPath Visualizer这样的工具。多年来,这个工具已经帮助了成千上万的人通过使用XPath表达式并观察其评估结果来以一种有趣的方式学习XPath。

注意:XPath Visualizer是我在2000年创建的,从未成为一款财务产品。我仅基于它对用户的价值,在多年的实践中证明了它的推荐价值。


2
您可以使用xpath ancestor::item

小错误:您需要倒转顺序以满足原帖作者的期望。 - Sean B. Durkin

1
你输出 bcdx 而不是 ab 的原因是因为你的 <xsl:for-each> 正在迭代所选 item 元素的所有子元素,而不仅仅是迭代 $test 变量中所选的元素。 将你的 for-each 更改为只迭代 $test
<xsl:variable name="curr" select="//item[@title = 'c']"></xsl:variable>
<xsl:variable name="test" select="$curr/ancestor::item"></xsl:variable>

<xsl:for-each select="$test">
    <xsl:value-of select="@title"></xsl:value-of>
</xsl:for-each>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接