每个元素最后一次出现的XPath



我有类似的XML

<root>
    <a>One</a>
    <a>Two</a>
    <b>Three</b>
    <c>Four</c>
    <a>Five</a>
    <b>
        <a>Six</a>
    </b>
</root>

并且需要选择根中任何子节点名称的最后一次出现。在这种情况下,所需的结果列表将是:

<c>Four</c>
<a>Five</a>
<b>
    <a>Six</a>
</b>

感谢您的帮助!

XPath 2.0解决方案和当前接受的答案都非常低效(O(N^2((

此解决方案具有亚线性复杂性:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:key name="kElemsByName" match="/*/*"
  use="name()"/>
 <xsl:template match="/">
  <xsl:copy-of select=
    "/*/*[generate-id()
         =
          generate-id(key('kElemsByName', name())[last()])
         ]"/>
 </xsl:template>
</xsl:stylesheet>

应用于所提供的XML文档时

<root>
    <a>One</a>
    <a>Two</a>
    <b>Three</b>
    <c>Four</c>
    <a>Five</a>
    <b>
        <a>Six</a>
    </b>
</root>

生成所需的正确结果

<c>Four</c>
<a>Five</a>
<b>
   <a>Six</a>
</b>

解释:这是Muenchian分组的一个修改变体,因此不是第一个。但是每个组中的最后一个节点被处理。

II XPath 2.0一行:

用途:

/*/*[index-of(/*/*/name(), name())[last()]]

使用XSLT 2.0作为XPath 2.0主机的验证

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:template match="/">
  <xsl:sequence select=
    "/*/*[index-of(/*/*/name(), name())[last()]]"/>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于相同的XML文档(之前提供(时,会产生相同的正确结果

<c>Four</c>
<a>Five</a>
<b>
    <a>Six</a>
</b>

如果可以使用XPath 2.0,这将适用于

/root//*[not(name() = following-sibling::*/name())]

基于XSLT的解决方案:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="root/*">
        <xsl:variable name="n" select="name()"/>
        <xsl:copy-of
            select=".[not(following-sibling::node()[name()=$n])]"/>
    </xsl:template>
</xsl:stylesheet>

生产输出:

<c>Four</c>
<a>Five</a>
<b>
   <a>Six</a>
</b>

第二个解决方案(您可以将其用作单个XPath表达式(:

<xsl:template match="/root">
    <xsl:copy-of select="a[not(./following-sibling::a)]
        | b[not(./following-sibling::b)]
        | c[not(./following-sibling::c)]"/>
</xsl:template>

如今,XSLT2.0为这类问题提供了分组技术:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes" />
    <xsl:strip-space elements="*" />
    <xsl:template match="/root">
        <xsl:for-each-group select="*" group-by="name()">
            <!-- <xsl:sort select="index-of(/root/*, current-group()[last()])" order="ascending"/> -->
            <xsl:copy-of select="current-group()[last()]" />
        </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>

将产生:

<a>Five</a>
<b>
  <a>Six</a>
</b>
<c>Four</c>

其中分组是按文档顺序进行的,除非受到<xsl:sort>的明确影响!

相关内容

最新更新