1

我有以下架构:

<parent>
  <child id="1" name="Child 1 Version 1" />
</parent>
<parent>
  <child id="2" name="Child 2 Version 1" />
</parent>
<parent>
  <child id="1" name="Child 1 Version 2" />
</parent>

我只想处理每个 id 的最后一个节点。以下是我根据一些阅读尝试过的内容:

  <xsl:for-each select="//parent/child">
    <xsl:sort select="@id"/>
    <xsl:if test="not(@id=following-sibling::*/@id)">
      <xsl:element name="child">
        <xsl:value-of select="@name"/>
      </xsl:element>
    </xsl:if>
  </xsl:for-each>

但这似乎不起作用。我的输出仍然包含所有三个元素。关于我可以做些什么来纠正我的问题的任何想法?

4

2 回答 2

2

我只想处理每个 id 的最后一个节点。以下是我根据一些阅读尝试过的内容:

  <xsl:for-each select="//parent/child"> 
    <xsl:sort select="@id"/> 
    <xsl:if test="not(@id=following-sibling::*/@id)"> 
      <xsl:element name="child"> 
        <xsl:value-of select="@name"/> 
      </xsl:element> 
    </xsl:if> 
  </xsl:for-each> 

但这似乎不起作用。我的输出仍然包含所有三个元素。关于我可以做些什么来纠正我的问题的任何想法?

这段代码的问题是,即使节点在排序的节点集中,它们following-sibling的 s 仍然是文档中的。

为了使这段代码能够工作,首先要创建一个全新的文档,其中的节点以所需的方式排序,然后(在 XSLT 1.0 中,必须使用xxx:node-set()生成的 RTF 上的扩展来使其成为普通的 XML 文档) 在本文档中,节点根据需要具有其兄弟姐妹。

解决方案

此转换提供了一种可能的 XSLT 1.0 解决方案,它不需要使用扩展函数:

<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="kchildById" match="child" use="@id"/>

 <xsl:template match="/*">
  <t>
    <xsl:apply-templates select=
    "*/child[generate-id()
            =
             generate-id(key('kchildById',
                             @id)[last()]
                         )
            ]
    "/>
  </t>
 </xsl:template>

 <xsl:template match="child">
  <child>
   <xsl:value-of select="@name"/>
  </child>
 </xsl:template>
</xsl:stylesheet>

当应用于提供的 XML 片段时(包装在顶部元素中以成为格式良好的 XML 文档并添加第二个版本id="2"):

<t>
    <parent>
        <child id="1" name="Child 1 Version 1" />
    </parent>
    <parent>
        <child id="2" name="Child 2 Version 1" />
    </parent>
    <parent>
        <child id="1" name="Child 1 Version 2" />
    </parent>
    <parent>
        <child id="2" name="Child 2 Version 2" />
    </parent>
</t>

产生想要的结果

<t>
   <child>Child 1 Version 2</child>
   <child>Child 2 Version 2</child>
</t>

请注意:使用 Muenchian 方法进行分组。

于 2010-10-21T01:34:06.060 回答
1

这个样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kParentByChildId" match="parent" use="child/@id"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="parent[count(.|key('kParentByChildId',
                                            child/@id)[last()]) != 1]"/>
</xsl:stylesheet>

输出:

<root>
    <parent>
        <child id="2" name="Child 2 Version 1"></child>
    </parent>
    <parent>
        <child id="1" name="Child 1 Version 2"></child>
    </parent>
</root>

注意。按@id 分组,选择组中的最后一个。

编辑:以防万一这令人困惑。上面的样式表意味着:复制所有内容,除了那些child没有最后一个@id相同类型的。因此,它不是选择组中的最后一个,而是作为反向逻辑,条带化不是组中的最后一个。

第二。为什么你的不工作?好吧,因为following-sibling轴。您找到第一个类型的方法是从过去很少有处理器实现密钥的时代开始的。现在那些日子已经一去不复返了。

所以,这个样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:template match="t">
        <xsl:for-each select="parent/child">
            <xsl:sort select="@id"/>
            <xsl:if test="not(@id=following::child/@id)">
                <xsl:element name="child">
                    <xsl:value-of select="@name"/>
                </xsl:element>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

输出:

<child>Child 1 Version 2</child>
<child>Child 2 Version 1</child>

注意following轴,因为child元素没有兄弟姐妹。

于 2010-10-20T20:55:35.387 回答