Search code examples
xslt-1.0xslt-2.0xslt-grouping

Grouping and select distinct in XSLT 1.0


I have an XML, i am trying to do kind of group by with XLST 1.0.

Input XMl:

<Rowset>
   
    <Row>
        <col1>7:00</col1>
        <name>Shell Test</name>
        <passCount>1</passCount>
        <failCount>0</failCount>
    </Row>
     <Row>
        <col1>7:00</col1>
        <name>Stroke Test</name>
        <passCount>1</passCount>
        <failCount>1</failCount>
    </Row>
     <Row>
        <col1>7:00</col1>
        <name>Shutoff Test</name>
        <passCount>0</passCount>
        <failCount>1</failCount>
    </Row>
    <Row>
        <col1>8:00</col1>
       <name>Shell Test</name>
        <passCount>0</passCount>
        <failCount>0</failCount>
    </Row>
      <Row>
        <col1>8:00</col1>
       <name>Stroke Test</name>
        <passCount>0</passCount>
        <failCount>0</failCount>
    </Row>
      <Row>
        <col1>8:00</col1>
        <name>Shutoff Test</name>
        <passCount>0</passCount>
        <failCount>0</failCount>
    </Row>
      <Row>
        <col1>9:00</col1>
       <name>Shell Test</name>
        <passCount>0</passCount>
        <failCount>0</failCount>
    </Row>
      <Row>
        <col1>9:00</col1>
       <name>Stroke Test</name>
        <passCount>0</passCount>
        <failCount>0</failCount>
    </Row>
      <Row>
        <col1>9:00</col1>
        <name>Shutoff Test</name>
        <passCount>0</passCount>
        <failCount>0</failCount>
    </Row>
   
   
</Rowset>

outPutXMl:

<?xml version="1.0" encoding="UTF-8"?>
<Row>
    <element>
        <TestName>Shell Test</TestName>
    </element>
    <element>
        <TestName>Stroke Test</TestName>
    </element>
    <element>
        <TestName>Shutoff Test</TestName>
    </element>
</Row>
<Row>
    <element>
        <Time>7:00</Time>
        <Pass>1</Pass>
        <Fail>0</Fail>
        <Pass>1</Pass>
        <Fail>1</Fail>
        <Pass>0</Pass>
        <Fail>1</Fail>
    </element>
    <element>
        <Time>8:00</Time>
        <Pass>0</Pass>
        <Fail>0</Fail>
        <Pass>0</Pass>
        <Fail>0</Fail>
        <Pass>0</Pass>
        <Fail>0</Fail>
    </element>
    <element>
        <Time>9:00</Time>
        <Pass>0</Pass>
        <Fail>0</Fail>
        <Pass>0</Pass>
        <Fail>0</Fail>
        <Pass>0</Pass>
        <Fail>0</Fail>
    </element>
</Row>

i am facing problem in extracting all the values from group in second for-each-group my xslt 1.0 is as below:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="col1name" match="Row" use="name" />
    <xsl:key name="time" match="Row" use="col1" />
    <xsl:template match="/Rowsets/Rowset">
        <Row>
             <xsl:for-each select="Row[generate-id() = generate-id(key('col1name', name)[1])]">
    
        <TestName><xsl:value-of select="name"/></TestName>
    
    </xsl:for-each>
        </Row>
        
        <xsl:for-each select="Row">
        <Time><xsl:value-of select="col1"/></Time>
                <xsl:apply-templates select="/Rowsets/Rowset"/>
        
        
    </xsl:for-each>
        
        
        
    </xsl:template>
    
    <xsl:template match="/Rowsets/Rowset">
    
        <xsl:for-each select="Row[generate-id() = generate-id(key('time', col1)[*])]">
    
        
        <Pass><xsl:value-of select="passCount"/></Pass>
        <Fail><xsl:value-of select="failCount"/></Fail>
    
   
    </xsl:for-each>
    </xsl:template>
    
</xsl:stylesheet>

with XSLT 2.0 i could achieve this, but as per my requirement my application only supports XSLT 1.0

XSLT 2.0 Code:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/Rowsets/Rowset">
        <Row>
            <xsl:for-each-group select="Row" group-by="name">
                <element>
                    <TestName>
                        <xsl:value-of select="name"/>
                    </TestName>
                </element>
            </xsl:for-each-group>
        </Row>
        <Row>
            <xsl:for-each-group select="Row" group-by="col1">
                <element>
                    <Time>
                        <xsl:value-of select="col1"/>
                    </Time>
                    <xsl:for-each select="current-group()">
                        <Pass>
                            <xsl:value-of select="passCount"/>
                        </Pass>
                        <Fail>
                            <xsl:value-of select="failCount"/>
                        </Fail>
                    </xsl:for-each>
                </element>
            </xsl:for-each-group>
        </Row>
        
    </xsl:template>
</xsl:stylesheet>

Can someone please help in replicating the output with XSLT 1.0


Solution

  • Use the two keys for Muenchian grouping:

    <xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output indent="yes"/>
        
        <xsl:key name="by-name" match="Row" use="name"/>
        
        <xsl:key name="by-col1" match="Row" use="col1"/>
        
        <xsl:template match="/Rowsets/Rowset">
            <Row>
                <xsl:for-each select="Row[generate-id() = generate-id(key('by-name', name)[1])]">
                    <element>
                        <TestName>
                            <xsl:value-of select="name"/>
                        </TestName>
                    </element>
                </xsl:for-each>
            </Row>
            <Row>
                <xsl:for-each select="Row[generate-id() = generate-id(key('by-col1', col1)[1])]">
                    <element>
                        <Time>
                            <xsl:value-of select="col1"/>
                        </Time>
                        <xsl:for-each select="key('by-col1', col1)">
                            <Pass>
                                <xsl:value-of select="passCount"/>
                            </Pass>
                            <Fail>
                                <xsl:value-of select="failCount"/>
                            </Fail>
                        </xsl:for-each>
                    </element>
                </xsl:for-each>
            </Row>
            
        </xsl:template>
    </xsl:stylesheet>