XSLT忽略重复的元素

时间:2013-09-19 14:38:43

标签: xml xslt xpath

我刚刚开始学习XSLT,并且无法忽略重复的元素。

我一直在搜索Stack Overflow并且看到人们提出类似的问题。我尝试了一个小例子,看看我的文件出错了,并且能够忽略重复的元素。然而,当我有一种以上的元素时,似乎会出现问题。

例如:

File1.xml

<?xml-stylesheet type="text/xsl" href="merge2.xsl"?>

<Main>
    <Records>
        <Record>
            <Description>A</Description>
        </Record>
        <Record>
            <Description>A</Description>
        </Record>
        <Record>
            <Description>B</Description>
        </Record>
        <Record>
            <Description>C</Description>
        </Record>
    </Records>
</Main>

merge2.xsl

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
    <table border="1">
        <tr>
            <th>Type</th>
            <th>Count</th>
        </tr>
        <xsl:for-each select="Main/Records/Record">
            <xsl:if test ="not(preceding-sibling::Record(Description/text() = current()/Description/text()])">
                <tr>
                    <td><xsl:value-of select="Description"/></td>
                    <td><xsl:value-of select="count(//Record[Description/text()=current()/Description/text()])"/></td>
                </tr>
            </xsl:if>
        </xsl:for-each>
    </table>
</xsl:template>
</xsl:stylesheet>

这很好,并且给了我想要的结果。

Type    Count
 A        2
 B        1
 C        1

但是,如果我要添加另一个记录元素,它似乎一个接一个地处理它们,例如

<?xml-stylesheet type="text/xsl" href="merge2.xsl"?>

<Main>
    <Records>
        <Record>
            <Description>A</Description>
        </Record>
        <Record>
            <Description>A</Description>
        </Record>
        <Record>
            <Description>B</Description>
        </Record>
        <Record>
            <Description>C</Description>
        </Record>
    </Records>
    <Records>
        <Record>
            <Description>B</Description>
        </Record>
        <Record>
            <Description>A</Description>
        </Record>
        <Record>
            <Description>C</Description>
        </Record>
        <Record>
            <Description>C</Description>
        </Record>
    </Records>
</Main>

这会产生以下结果。

Type        Count
 A            3
 B            2
 C            3
 B            2
 A            3
 C            3

它似乎处理第一个记录实例,然后转到下一个。有没有办法让它能够消除两者之间的重复?

我已经尝试更改for-each以浏览每个Records实例,并尝试为它创建一个单独的模板,但是我似乎仍然遗漏了一些东西,因为我还没有设法让它工作。

非常感谢您的帮助。

2 个答案:

答案 0 :(得分:4)

尝试使用preceding-sibling中的preceding替换xsl:if statement

您有正确的想法来制作测试,以便每个遇到的tr值只发出一次Description。但是,preceding-sibling会停止通过父母的直接子女进行检查; preceding在文档的前面继续检查,这是您希望避免Records之间的重复。

Fyi,还有一个错字Record(应该是Record[。这是一个完整的,可操作的转型,包括这些改变:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
  <xsl:template match="/">
    <table border="1">
      <tr>
        <th>Type</th>
        <th>Count</th>
      </tr>
      <xsl:for-each select="Main/Records/Record">
        <xsl:if test ="not(preceding::Record[Description/text() = current()/Description/text()])">
          <tr>
            <td><xsl:value-of select="Description"/></td>
            <td><xsl:value-of select="count(//Record[Description/text()=current()/Description/text()])"/></td>
          </tr>
        </xsl:if>
      </xsl:for-each>
    </table>
  </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:1)

即使@kjhughes能够回答您的问题,但为了更有效的方法,您还需要使用Muenchain Method

为此,您需要定义一个分组键

<xsl:key name="Record-by-Description" match="Record" use="Description"/>

然后使用该键对这些项目进行分组

<xsl:apply-templates select="Record[generate-id() = generate-id(key('Record-by-Description', Description)[1])]" mode="group"/>

然后使用该密钥仅计算那些特定项目

<xsl:value-of select="count(key('Record-by-Description', Description))"/>

此过程效率更高,每次都不需要导航整个结构。

所以,总而言之,当你采用这个XML

<Main>
  <Records>
    <Record>
      <Description>A</Description>
    </Record>
    <Record>
      <Description>A</Description>
    </Record>
    <Record>
      <Description>B</Description>
    </Record>
    <Record>
      <Description>C</Description>
    </Record>
  </Records>
  <Records>
    <Record>
      <Description>B</Description>
    </Record>
    <Record>
      <Description>A</Description>
    </Record>
    <Record>
      <Description>C</Description>
    </Record>
    <Record>
      <Description>C</Description>
    </Record>
  </Records>
</Main>

并将此XSLT应用于它

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
  <xsl:output method="xml" indent="yes" />
  <xsl:key name="Record-by-Description" match="Record" use="Description"/>

  <xsl:template match="@* | node()">
    <xsl:apply-templates select="@* | node()"/>
  </xsl:template>

  <xsl:template match="Main">
    <table>
      <tr>
        <th>Type</th>
        <th>Count</th>
      </tr>
      <xsl:apply-templates select="Records"/>
    </table>
  </xsl:template>

  <xsl:template match="Records">
    <xsl:apply-templates select="Record[generate-id() = generate-id(key('Record-by-Description', Description)[1])]" mode="group"/>
  </xsl:template>

  <xsl:template match="Record" mode="group">
    <tr>
      <td>
        <xsl:value-of select="Description"/>
      </td>
      <td>
        <xsl:value-of select="count(key('Record-by-Description', Description))"/>
      </td>
    </tr>
  </xsl:template>

</xsl:stylesheet>

它产生此输出

<table>
  <tr>
    <th>Type</th>
    <th>Count</th>
  </tr>
  <tr>
    <td>A</td>
    <td>3</td>
  </tr>
  <tr>
    <td>B</td>
    <td>2</td>
  </tr>
  <tr>
    <td>C</td>
    <td>3</td>
  </tr>
</table>