0
votes

I want to group nodes with same name in Parent tag(<Item>) as well as in child tag(<Sku>).

<Item> tag might contain many <Sku> child tags but those should not be grouped, rather the elements in every Sku and Item should be grouped individually.

I have an input xml file like below:

<Products>
  <Item>
    <Dimensions>
      <Height>10</Height>
    </Dimensions>
    <Dimensions>
      <Weight>10</Weight>
    </Dimensions>
    <Color>
      <Attribute>Orange</Attribute>
    </Color>
    <Color>
      <Attribute>Blue</Attribute> 
    </Color>
    <Sku>
     <Dimensions>
      <Height>10</Height>
     </Dimensions>
     <Dimensions>
      <Weight>10</Weight>
     </Dimensions>
     <Color>
      <Attribute>Orange</Attribute>
     </Color>
     <Color>
      <Attribute>Blue</Attribute> 
     </Color>
    </Sku>
    <Sku>
     <Dimensions>
      <Height>10</Height>
     </Dimensions>
     <Dimensions>
      <Weight>10</Weight>
     </Dimensions>
     <Color>
      <Attribute>Orange</Attribute>
     </Color>
     <Color>
      <Attribute>Blue</Attribute> 
     </Color>
    </Sku>
   </Item>
</Products>   

Output expected is like below:

<Products>
  <Item>
        <Dimensions>
            <Height>10</Height>
            <Weight>10</Weight>
        </Dimensions>
        <Color>
            <Attribute>Orange</Attribute>
            <Attribute>Blue</Attribute>
        </Color>
        <Sku>
        <Dimensions>
            <Height>10</Height>
            <Weight>10</Weight>
        </Dimensions>
        <Color>
            <Attribute>Orange</Attribute>
            <Attribute>Blue</Attribute>
        </Color>
     </Sku>
     <Sku>
        <Dimensions>
            <Height>10</Height>
            <Weight>10</Weight>
        </Dimensions>
        <Color>
            <Attribute>Orange</Attribute>
            <Attribute>Blue</Attribute>
        </Color>
    </Sku>
  </Item>
</Products>

Any help would be greatly appreciated. I have used below xslt to convert but it is only gouping elements present under 'Item'.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="elements" match="Item/*[not(self::Sku)]" use="concat(name(), '|', generate-id(..))"/>

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

<xsl:template match="Item">
    <xsl:copy>
        <xsl:apply-templates select="@*" />
        <xsl:for-each select="*[generate-id() = generate-id(key('elements', concat(name(), '|', generate-id(..)))[1])]">
            <xsl:copy>
                <xsl:apply-templates select="key('elements', concat(name(), '|', generate-id(..)))/*"/>
            </xsl:copy>
        </xsl:for-each>
        <xsl:apply-templates select="Item" />
    </xsl:copy>
</xsl:template>
</xsl:stylesheet> 
1

1 Answers

0
votes

You can share the same logic between both groupings, like this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:key name="elements" match="*" use="concat(name(), '|', generate-id(..))"/>

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

  <xsl:template name="GroupChildren">
    <xsl:param name="elements" select="*" />

    <xsl:for-each select="$elements[generate-id() = 
                                    generate-id(key('elements', 
                                                    concat(name(), '|', 
                                                           generate-id(..))
                                                   )[1])]">
      <xsl:copy>
        <xsl:apply-templates select="key('elements', 
                                         concat(name(), '|', generate-id(..)))/*"/>
      </xsl:copy>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match="Item">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:call-template name="GroupChildren">
        <xsl:with-param name="elements" select="*[not(self::Sku)]" />
      </xsl:call-template>
      <xsl:apply-templates select="Sku" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Sku">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:call-template name="GroupChildren" />
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

When run on your sample input, the result is:

<Products>
  <Item>
    <Dimensions>
      <Height>10</Height>
      <Weight>10</Weight>
    </Dimensions>
    <Color>
      <Attribute>Orange</Attribute>
      <Attribute>Blue</Attribute>
    </Color>
    <Sku>
      <Dimensions>
        <Height>10</Height>
        <Weight>10</Weight>
      </Dimensions>
      <Color>
        <Attribute>Orange</Attribute>
        <Attribute>Blue</Attribute>
      </Color>
    </Sku>
    <Sku>
      <Dimensions>
        <Height>10</Height>
        <Weight>10</Weight>
      </Dimensions>
      <Color>
        <Attribute>Orange</Attribute>
        <Attribute>Blue</Attribute>
      </Color>
    </Sku>
  </Item>
</Products>