Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT skip duplicate element

I am a beginner to XSLT.

My Source XML is as below:

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

The out-put should be as below:

<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>

I have created XSLT as below

<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>

With above XSLT i got the below output:

<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>

How can i omit or skip the duplicate element? Please help.

like image 344
Ankur Raiyani Avatar asked Jan 15 '23 05:01

Ankur Raiyani


1 Answers

This is actually a good example of a grouping problem. In XSLT1.0, the most efficient way to do grouping is with a technique called "Muenchian Grouping", so it might be worthwhile learning about this.

In this case, you want to group Passenger elements by their @type attribute, so you would define a key to do this

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

Then, you need to select the Passenger elements which happen to be the first occurence of that element in the group for their @type attribute. This is done as follows:

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

Note the use of generate-id which generates a unique ID for a node, allowing two nodes to be compared.

Then, to count the number of occurences in the group, it is straight-forward

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

Here is the full 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>

When applied to your sample XML, the following is output

<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>

Also note there is no real reason to use xsl:element to output static elements. Just write out the element directly.

like image 85
Tim C Avatar answered Feb 07 '23 01:02

Tim C