XML transformation using XSLT by grouping the tags based on some value

464 Views Asked by At

I am trying to transform one XML by creating a new tag by grouping the tags. like group the items which are having same parent value.

 <root>
   <item id="100">
      <properties>
         <property attribute-id="parent">10</property>
      </properties>
      <properties>
         <property attribute-id="amount">1.1</property>
      </properties>
   </item>
   <item id="101">
      <properties>
         <property attribute-id="parent">10</property>
      </properties>
      <properties>
         <property attribute-id="amount">1.1</property>
      </properties>
   </item>
   <item id="102">
      <properties>
         <property attribute-id="parent">11</property>
      </properties>
      <properties>
         <property attribute-id="amount">1.1</property>
      </properties>
   </item>
   <item id="103">
      <properties>
         <property attribute-id="parent">10</property>
      </properties>
      <properties>
         <property attribute-id="amount">1.1</property>
      </properties>
   </item>
   <item id="104">
      <properties>
         <property attribute-id="parent">11</property>
      </properties>
      <properties>
         <property attribute-id="amount">1.1</property>
      </properties>
   </item>
</root>

New tag should be:

<item id = "10">  
       <childs>
         <child id="100" />
         <child id="101" />
         <child id="102" />
       </childs>
    </item>       
    <item id = "11">
       <childs>
         <child id="104" />
         <child id="105" />
       </childs>
    </item> 

Is this possible with XSLT?
How can this be done in XSLT?

Editing the initial post i have faced some issues when the tag was in attribute format. Tried updating the first solution but the attribute form is causing many problems.

1

There are 1 best solutions below

2
zx485 On BEST ANSWER

You can achieve with this the XSLT-1.0 method Muenchian Grouping. If you search on SO, you'll find a lot of examples. Applying this method, your stylesheet could look like this (I added a hypothetical <root> element around your source XML to make it well-formed):

The xsl:key and the select expression of the outer xsl:for-each do implement the Muenchian Grouping. The inside of the loop should be kind of self-explaining.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="id" match="item" use="properties/property[@attribute-id='parent']" />   

<xsl:template match="/root">
    <xsl:copy>
        <xsl:for-each select="item[generate-id() = generate-id(key('id',properties/property[@attribute-id='parent'])[1])]">
            <item id="{parent}">
                <childs>
                    <xsl:for-each select="key('id',properties/property[@attribute-id='parent'])">
                        <child id="{@id}" />
                    </xsl:for-each>
                </childs>
            </item>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

If you can use XSLT-2.0 or above, you can make use of xsl:for-each-group like this:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>

<xsl:template match="/root">
    <xsl:copy>
        <xsl:for-each-group select="item" group-by="properties/property[@attribute-id='parent']">
            <item id="{parent}">
                <childs>
                    <xsl:for-each select="current-group()">
                        <child id="{@id}" />
                    </xsl:for-each>
                </childs>
            </item>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

It is slightly more simple, but does the same.


Its output, in both cases, is:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <item id="">
      <childs>
         <child id="100"/>
         <child id="101"/>
         <child id="103"/>
      </childs>
   </item>
   <item id="">
      <childs>
         <child id="102"/>
         <child id="104"/>
      </childs>
   </item>
</root>

It's not exactly what you want, but I guess that the last child id's of your example are wrong.