我有一个像
这样的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,如上例所示,示例中的主节点可能是上面另一个元素节点的一部分。
感谢 ķ
答案 0 :(得分:2)
可能的表达方式是
.//element[not(ancestor::element)]
即。查找未被另一个“element”元素包围(在任何级别)的所有“元素”元素。
修改强>
如上所述,如果当前(main
)元素本身被“元素”包围,则此表达式不起作用。如果你在XSLT中,因此可以访问current()
函数,那么你可以使用像
.//element[count(ancestor::element) = count(current()/ancestor::element)]
选择那些与当前“main”元素具有相同数量的封闭“元素”元素的元素(可以为零)。根据您的处理器,使用变量
可能更有效和/或更清晰<xsl:variable name="depth" select="count(ancestor::element)" />
<xsl:apply-templates select=".//element[count(ancestor::element) = $depth]"/>
答案 1 :(得分: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]
]
选择当前节点的所有descendat element
元素,两个可能的祖先中的第一个(向上)main
或element
为main
。
也就是说,没有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
]"/>
答案 2 :(得分:1)
我认为这个答案比Dimitre更严格,因为它没有假设缺少其他名为“main”的元素,而这些元素并没有被指定为问题的一部分。
for $this in . return .//element[empty(ancestor::element[.>>$this])]
如果您使用的是XSLT,则可以使用current()代替$ this。