XSLT跳过重复元素

时间:2012-10-25 12:53:05

标签: java xml transformation xslt

我是XSLT的初学者。

My Source XML如下:

<Passengers>
    <Passenger type="A" id="P1"/>
    <Passenger type="A" id="P2"/>
    <Passenger type="B" id="P3"/>
    <Passenger type="C" id="P4"/>
</Passengers>

输出应如下:

<Pax_Items>
     <Item>
         <Type>A</Type>
         <Count>2</Count>
     </Item>
     <Item>
         <Type>B</Type>
         <Count>1</Count>
     </Item>
     <Item>
         <Type>C</Type>
         <Count>1</Count>
     </Item>
</Pax_Items>

我已经创建了XSLT,如下所示

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0" exclude-result-prefixes="xmlns">

    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
        <xsl:variable name="filter" select="'TK,AJ'"/>

    <xsl:template match="Passengers">
        <xsl:element name="Pax_Items">
            <xsl:apply-templates select="Passenger"/>
        </xsl:element>
    </xsl:template>

        <xsl:template match="Passenger">
              <xsl:element name="Item">
                     <xsl:element name="Type">
                           <xsl:value-of select="@type"/>
                     </xsl:element>
                     <xsl:element name="Count">
                           <xsl:value-of select="count(//Passenger[@type=current()/@type])"/>
                     </xsl:element>
              </xsl:element>
    </xsl:template>
</xsl:stylesheet>

使用上面的XSLT我得到了以下输出:

<Pax_Items>
  <Item>
    <Type>A</Type>
    <Count>2</Count>
  </Item>
  <Item>
    <Type>A</Type>
    <Count>2</Count>
  </Item>
  <Item>
    <Type>B</Type>
    <Count>1</Count>
  </Item>
  <Item>
    <Type>C</Type>
    <Count>1</Count>
  </Item>
</Pax_Items>

如何省略或跳过重复元素?请帮忙。

2 个答案:

答案 0 :(得分:7)

这实际上是分组问题的一个很好的例子。在XSLT1.0中,最有效的分组方法是使用一种称为“Muenchian分组”的技术,因此可能值得学习这一点。

在这种情况下,您希望按 @type 属性对 Passenger 元素进行分组,因此您需要定义一个键来执行此操作

<xsl:key name="Passengers" match="Passenger" use="@type"/>

然后,您需要选择 Passenger 元素,这些元素恰好是 @type 属性中该元素首次出现的元素。这样做如下:

<xsl:apply-templates 
    select="Passenger[generate-id() = generate-id(key('Passengers', @type)[1])]"/>

请注意使用 generate-id 为节点生成唯一ID,允许比较两个节点。

然后,要计算组中出现的次数,它是直截了当的

<xsl:value-of select="count(key('Passengers', @type))"/>

这是完整的XSLT

<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:key name="Passengers" match="Passenger" use="@type"/>

   <xsl:template match="Passengers">
      <Pax_Items>
         <xsl:apply-templates select="Passenger[generate-id() = generate-id(key('Passengers', @type)[1])]"/>
      </Pax_Items>
   </xsl:template>

   <xsl:template match="Passenger">
      <Item>
         <Type>
            <xsl:value-of select="@type"/>
         </Type>
         <Count>
            <xsl:value-of select="count(key('Passengers', @type))"/>
         </Count>
      </Item>
   </xsl:template>
</xsl:stylesheet>

当应用于您的示例XML时,输出以下内容

<Pax_Items>
   <Item>
      <Type>A</Type>
      <Count>2</Count>
   </Item>
   <Item>
      <Type>B</Type>
      <Count>1</Count>
   </Item>
   <Item>
      <Type>C</Type>
      <Count>1</Count>
   </Item>
</Pax_Items>

另请注意,没有理由使用 xsl:element 来输出静态元素。直接写出元素。

答案 1 :(得分:2)

更新您的passenger模板,如下所示;我添加了if条件来检查重复的节点,

<xsl:template match="Passenger">
    <xsl:if test="not(preceding-sibling::Passenger[@type = current()/@type])">
      <xsl:element name="Item">
        <xsl:element name="Type">
          <xsl:value-of select="@type"/>
        </xsl:element>
        <xsl:element name="Count">
          <xsl:value-of select="count(//Passenger[@type=current()/@type])"/>
        </xsl:element>
      </xsl:element>
    </xsl:if>
  </xsl:template>