2

我有一个 XML 片段

<main>
    <sub1>
        <element>
            <sub2>
                <element>
                    <sub3/>
                </element>
            </sub2>
        </element>
    </sub1>
    <sub4>
        <sub5>
            <element>
                <sub6>
                    <element>
                        <sub7/>
                    </element>
                </sub6>
            </element>
        </sub5>
    </sub4>
    <sub8>
        <element/>
    </sub8>
</main>

如果 main 是当前节点,我可以像这样选择所有元素节点:

select=".//element"

在这个例子中,这给了我 5 个匹配项。但是如何只选择层次结构中的第一个元素节点,那么 sub1、sub5 和 sub8 之后的元素节点呢?我需要忽略在它们和原始主节点之间有另一个元素的所有元素。

其间的子节点的数量可以多于1个或2个,如上例中,示例中的主节点可以是上述另一个元素节点的一部分。

谢谢K

4

3 回答 3

2

一个可能的表达是

.//element[not(ancestor::element)]

即找到所有未被另一个“元素”元素包围(在任何级别)的“元素”元素。

编辑

如前所述,如果当前 ( main) 元素本身被“元素”包围,则此表达式不会按原样工作。如果您在 XSLT 中并因此可以访问该current()函数,那么您可以使用类似的技巧

.//element[count(ancestor::element) = count(current()/ancestor::element)]

它选择那些与当前“主”元素具有相同数量的封闭“元素”元素(可能为零)的元素。根据您的处理器,使用变量可能更有效和/或更清晰

<xsl:variable name="depth" select="count(ancestor::element)" />
<xsl:apply-templates select=".//element[count(ancestor::element) = $depth]"/>
于 2012-09-03T21:58:03.777 回答
1

使用

.//element[(ancestor::main|ancestor::element)[last()][self::main]]

这是使用 XSLT 进行的简单验证

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
  <xsl:for-each select=
   ".//element
       [(ancestor::main|ancestor::element)
                          [last()][self::main]
       ]">
   <xsl:copy-of select="."/>
==================
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时:

<main>
    <sub1>
        <element>
            <sub2>
                <element>
                    <sub3/>
                </element>
            </sub2>
        </element>
    </sub1>
    <sub4>
        <sub5>
            <element>
                <sub6>
                    <element>
                        <sub7/>
                    </element>
                </sub6>
            </element>
        </sub5>
    </sub4>
    <sub8>
        <element/>
    </sub8>
</main>

Xpath 表达式在当前节点是文档中的顶部节点的情况下进行评估,然后使用方便注意的分隔符字符串将此评估的结果复制到输出:

<element>

   <sub2>

      <element>

         <sub3/>

      </element>

   </sub2>

</element>
==================
  <element>

   <sub6>

      <element>

         <sub7/>

      </element>

   </sub6>

</element>
==================
  <element/>
==================

说明

表达方式:

    .//element
       [(ancestor::main|ancestor::element)
                                     [last()]
                                        [self::main]
       ]

element选择当前节点的所有后代元素,两个可能的祖先中的第一个(向上)main或者elementmain

也就是说,没有一个element是所选的祖先element并且在祖先之前发生(向上)main

要理解为什么last()被用作谓词而不仅仅是[1],我们需要记住两个祖先中最近的一个是它们中的最后一个。

请注意

这个 XPath 表达式正确地选择了所有想要element的元素,即使在当前节点本身有一个element祖先的情况下也是如此。

类似于 Ian Roberts 在回答中提出的表达式

.//element[not(ancestor::element)] 

在这种情况下不选择任何内容


更新

正如 Michael Kay 所指出的,如果我们进一步概括这个问题并允许其他main元素出现在文档中的任何位置,那么这个答案中提出的表达式可能会选择“误报”。

这是一个更新的表达式(不再是纯/独立的 XPath 表达式,因为它使用 XSLT 函数current())在这种情况下仍然产生正确的计数

.//element
   [(ancestor::main|ancestor::element)
                      [last()][self::main]
  and
    count(current()|ancestor::main[1]) = 1
   ]"/>
于 2012-09-03T21:56:48.727 回答
1

我认为这个答案比 Dimitre 的答案更严格一些,因为它没有假设没有其他名为“main”的元素,而这些元素没有被指定为问题的一部分。

for $this in . return .//element[empty(ancestor::element[.>>$this])]

如果您在 XSLT 中,您可以使用 current() 代替 $this。

于 2012-09-04T07:46:44.327 回答