0
votes

I need help with sub-grouping in XSLT 1.0. I have following input XML

<Items>
<Item>
    <DAYRATE>12.00</DAYRATE>
    <WEEKRATE>13.00</WEEKRATE>
    <MONTHRATE>14.00</MONTHRATE>
    <MAJOR>Major 1</MAJOR>
    <MINOR>Minor 1</MINOR>
    <NAME>Name 1</NAME>
</Item>
<Item>
    <DAYRATE>15.00</DAYRATE>
    <WEEKRATE>16.00</WEEKRATE>
    <MONTHRATE>17.00</MONTHRATE>
    <MAJOR>Major 2</MAJOR>
    <MINOR>Minor 2</MINOR>
    <NAME>Name 1</NAME>
</Item>
<Item>
    <DAYRATE>25.00</DAYRATE>
    <WEEKRATE>26.00</WEEKRATE>
    <MONTHRATE>27.00</MONTHRATE>
    <MAJOR>Major 2</MAJOR>
    <MINOR>Minor 2</MINOR>
    <NAME>Name 2</NAME>
</Item></Items>

My desired output will be like below

<Items>
<Item>
    <Major>Major 1</Major>
    <Detail>
        <Minor>Minor 1</Minor>
        <Info>
            <Name>Name 1</Name>
            <DayRate>12.00</DayRate>
            <WeekRate>13.00</WeekRate>
            <MonthRate>14.00</MonthRate>
        </Info>
    </Detail>
</Item>
<Item>
    <Major>Major 2</Major>
    <Detail>
        <Minor>Minor 2</Minor>
        <Info>
            <Name>Name 1</Name>
            <DayRate>15.00</DayRate>
            <WeekRate>16.00</WeekRate>
            <MonthRate>17.00</MonthRate>
        </Info>
        <Info>
            <Name>Name 2</Name>
            <DayRate>25.00</DayRate>
            <WeekRate>26.00<WeekRate>
            <MonthRate>27.00</MonthRate>
        </Info>
    </Detail>
</Item>

Basically, I would like to group it by Major first and then by Minor. Looking for XSLT 1.0 solution.

Would appreciate any help.

Thank you.

I tried with following XSLT but the Minor grouping is not working.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0"/>

<xsl:key name="minor-cat" match="/Items/Item" use="MINOR/text()"/>

<xsl:template match="/Items">
    <Items>
        <xsl:for-each select="key('minor-cat', Item/MINOR/text())">
            <Item>
                <Major>
                    <xsl:value-of select="MAJOR/text()"/>
                </Major>
                <Detail>
                    <Minor>
                        <xsl:value-of select="MINOR/text()"/>
                    </Minor>
                    <Info>
                        <Name>
                            <xsl:value-of select="NAME/text()"/>
                        </Name>
                        <DayRate>
                            <xsl:value-of select="DAYRATE/text()"/>
                        </DayRate>
                        <WeekRate>
                            <xsl:value-of select="WEEKRATE/text()"/>
                        </WeekRate>
                        <MonthRate>
                            <xsl:value-of select="MONTHRATE/text()"/>
                        </MonthRate>
                    </Info>
                </Detail>
            </Item>
        </xsl:for-each>
    </Items>
</xsl:template>

2

2 Answers

0
votes

In XSLT-1.0 you have to use Muenchian Grouping with a composite key to achieve this.

You only have to make four changes to your XSLT:

  • one to the key creating a composite key
  • one to the for-each to realize Muenchian Grouping
  • creating a number type sort-key which is sensitive to decimal format from the composite key
  • adding this sort-key to sort the output by the number created above

This is the complete XSLT-1.0:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0"/>

<!-- Modified to use a composite key -->
<xsl:key name="minor-cat" match="/Items/Item" use="concat(MAJOR/text(),'.',MINOR/text())"/>          

<xsl:template match="/Items">
    <Items>
        <!-- Modified to use Muenchian Grouping -->
        <xsl:for-each select="Item[generate-id() = generate-id(key('minor-cat', concat(MAJOR/text(),'.',MINOR/text()))[1])]">   
            <!-- Added to sort by key -->
            <xsl:sort select="number(concat(substring-after(MAJOR/text(),' '),'.',substring-after(MINOR/text(),' ')))" data-type="number" /> 
            <!-- Process all elements with the same Major/Minor value -->
            <xsl:for-each select="key('minor-cat', concat(MAJOR/text(),'.',MINOR/text()))">
                <Item>
                    <Major>
                        <xsl:value-of select="MAJOR/text()"/>
                    </Major>
                    <Detail>
                        <Minor>
                            <xsl:value-of select="MINOR/text()"/>
                        </Minor>
                        <Info>
                            <Name>
                                <xsl:value-of select="NAME/text()"/>
                            </Name>
                            <DayRate>
                                <xsl:value-of select="DAYRATE/text()"/>
                            </DayRate>
                            <WeekRate>
                                <xsl:value-of select="WEEKRATE/text()"/>
                            </WeekRate>
                            <MonthRate>
                                <xsl:value-of select="MONTHRATE/text()"/>
                            </MonthRate>
                        </Info>
                    </Detail>
                </Item>
            </xsl:for-each>
        </xsl:for-each>
    </Items>
</xsl:template>
</xsl:stylesheet>
0
votes
<xsl:template match="Items">
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Item">
        <xsl:copy>
            <xsl:apply-templates select="MAJOR"/>
            <xsl:apply-templates select="MINOR"/>
            <xsl:apply-templates select="NAME"/>
            <xsl:apply-templates select="DAYRATE|WEEKRATE|MONTHRATE"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="MAJOR">
        <xsl:copy>
            <xsl:value-of select="."/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="MINOR">
        <DETAIL>
        <xsl:copy>
            <xsl:value-of select="."/>
        </xsl:copy>
            <xsl:apply-templates select=" following-sibling::NAME"/>
        </DETAIL>
    </xsl:template>
    <xsl:template match="NAME">
        <INFO>
        <xsl:copy>
            <xsl:value-of select="."/>
        </xsl:copy>
            <xsl:apply-templates select="preceding-sibling::DAYRATE|preceding-sibling::WEEKRATE|preceding-sibling::MONTHRATE"/>
        </INFO>
    </xsl:template>
    <xsl:template match="DAYRATE">
        <xsl:copy>
            <xsl:value-of select="."/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="WEEKRATE">
        <xsl:copy>
            <xsl:value-of select="."/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="MONTHRATE">
        <xsl:copy>
            <xsl:value-of select="."/>
        </xsl:copy>
    </xsl:template>
Check it if it is useful for you