bogdan
bogdan

Reputation: 27

xml grouping using xslt

i have a problem grouping some tags from an XML file.I wanna group the elements after the tag nr , and put them in a new tag masini. I have at the input this XML:

   <nota>
<auto>
<nr> 1 </nr>
<bmw>masina tare</bmw>
<mercedes> masina tiganeasca</mercedes>
<dacia> masina romaneasca</dacia>
</auto>
<auto>
<nr> 12 </nr>
<bmw>2041</bmw>
<mercedes> masina tdadsa</mercedes>
<dacia> masina veche</dacia>
</auto>

<auto>
<nr> 1 </nr>
<bmw>masina tare</bmw>
<mercedes> masina tiganeasca</mercedes>
<dacia> masina romaneasca</dacia>
</auto>
</nota>

I wanna get:

    <nota>
<masini>
<auto>
<nr> 1 </nr>
<bmw>masina tare</bmw>
<mercedes> masina tiganeasca</mercedes>
<dacia> masina romaneasca</dacia>
</auto>
<auto>
<nr> 1 </nr>
<bmw>masina tare</bmw>
<mercedes> masina tiganeasca</mercedes>
<dacia> masina romaneasca</dacia>
</auto>
</masini>
<masini>
<auto>
<nr> 12 </nr>
<bmw>2041</bmw>
<mercedes> masina tdadsa</mercedes>
<dacia> masina veche</dacia>
</auto>
</masini>
</nota>

If there are two nr equal there should be two auto tags with their element in the same masini tag. Sorry for me English and thanks in advance.

Upvotes: 0

Views: 110

Answers (1)

Tim C
Tim C

Reputation: 70618

In XSLT1.0, the most efficient technique is called Muenchian Grouping.

To start with, you define an xsl:key which will be used to look up the items in the group. In this case, you are looking for auto elements with the name nr element value:

<xsl:key name="auto" match="auto" use="nr" />

Next, you need to look for the auto elements which are the first elements in the group (i.e. they contain first occurence of each distinct nr element*

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

Then, for each such 'distinct' element, you can have a template match to put all the elements in the group within a new element

<xsl:template match="auto" mode="group">
   <masini>
      <xsl:apply-templates select="key('auto', nr)" />
   </masini>
</xsl:template>

Here is the full XSLT

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

   <xsl:template match="/*">
      <xsl:copy>
         <xsl:apply-templates select="auto[generate-id() = generate-id(key('auto', nr)[1])]" mode="group" />
      </xsl:copy>
   </xsl:template>

   <xsl:template match="auto" mode="group">
      <masini>
         <xsl:apply-templates select="key('auto', nr)" />
      </masini>
   </xsl:template>

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

When applied to your sample XML, the following is output

<nota>
   <masini>
      <auto>
         <nr> 1 </nr>
         <bmw>masina tare</bmw>
         <mercedes> masina tiganeasca</mercedes>
         <dacia> masina romaneasca</dacia>
      </auto>
      <auto>
         <nr> 1 </nr>
         <bmw>masina tare</bmw>
         <mercedes> masina tiganeasca</mercedes>
         <dacia> masina romaneasca</dacia>
      </auto>
   </masini>
   <masini>
      <auto>
         <nr> 12 </nr>
         <bmw>2041</bmw>
         <mercedes> masina tdadsa</mercedes>
         <dacia> masina veche</dacia>
      </auto>
   </masini>
</nota>

Also, note the use of the Identity Transform to copy all existing elements within the XML.

Upvotes: 1

Related Questions