Outputting a new row based on batch size in XSLT 1.0

Viewed 49

I'm using xslt to transform an xml into a pipe delimited file, xslt version 1.0. One person can have many classes. I'm struggling to figure out how to create a new row in batches of 5 classes in version 1.0?

Example code snippet

<root>
    <Person>
        <Name>Doug</Name>
        <Class>
            <ClassID>AS</ClassID>
        </Class>
        <Class>
            <ClassID>BA</ClassID>
        </Class>
        <Class>
            <ClassID>ZX</ClassID>
        </Class>
        <Class>
            <ClassID>MSN</ClassID>
        </Class>
        <Class>
            <ClassID>ABSN</ClassID>
        </Class>
        <Class>
            <ClassID>FS</ClassID>
        </Class>
    </Person>

    <Person>
        <Name>Amy</Name>
        <Class>
            <ClassID>AS</ClassID>
        </Class>
        <Class>
            <ClassID>BA</ClassID>
        </Class>
    </Person>
    
    <Person>
        <Name>Charlotte</Name>
        <Class>
            <ClassID>BA</ClassID>
        </Class>
    </Person>
    
    <Person>
        <Name>Betty</Name>
        <Class>
            <ClassID>ZX</ClassID>
        </Class>
    </Person>
</root>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
>

    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>
    <xsl:variable name="linefeed" select="'&#xA;'"/>
    

    <xsl:template match="/">

        <File>
            <Body>
                <xsl:apply-templates select="root/Person"/>
            </Body>
        </File>
    </xsl:template>

    <xsl:template match="root/Person">
            <xsl:value-of select="Name"/>
        
            <xsl:for-each select="Class">
                <xsl:text>|</xsl:text>
                <xsl:value-of select="ClassID" />
            </xsl:for-each>
            <xsl:value-of select="$linefeed"/>
    </xsl:template>


    </xsl:stylesheet>

Instead of this

Doug|AS|BA|ZX|MSN|ABSN|FS
Amy|AS|BA
Charlotte|BA
Betty|ZX

I need this- where Doug now has two rows because he has more than 5 classes (only 5 classes can fit on a line). This needs to be dynamic, there could be 100 classes.

Doug|AS|BA|ZX|MSN|ABSN
Doug|FS
Amy|AS|BA
Charlotte|BA
Betty|ZX

I've been going in circles- Help please!

2 Answers

You could do it with a recursive template like this :

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
>

    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>
    <xsl:variable name="linefeed" select="'&#xA;'"/>
    

    <xsl:template match="/">
        <xsl:apply-templates select="root/Person"/>
    </xsl:template>

    <xsl:template match="Person">
        <xsl:call-template name="PersonOutput">
            <xsl:with-param name="index" select="1"/>
            <xsl:with-param name="person" select="."/>
        </xsl:call-template>
    </xsl:template>
    
    <xsl:template name="PersonOutput">
        <xsl:param name="index"/>
        <xsl:param name="person"/>
        <xsl:if test="$index=1 or ($index mod 5)=1">
            <xsl:value-of select="$linefeed"/>
            <xsl:value-of select="$person/Name"/>
        </xsl:if>
        <xsl:text>|</xsl:text>
        <xsl:value-of select="$person/Class[$index]/ClassID"/>
        
        <xsl:if test="$person/Class[$index]/following-sibling::Class">
            <xsl:call-template name="PersonOutput">
                <xsl:with-param name="index" select="$index+1"/>
                <xsl:with-param name="person" select="$person"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

See it working here : https://xsltfiddle.liberty-development.net/3Mvnt4c

Here's a rather simple way you could look at it:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="/root">
    <xsl:for-each select="Person">
        <xsl:variable name="name" select="Name" />
        <xsl:for-each select="Class[position() mod 5 = 1]" >
            <xsl:value-of select="$name" />
            <xsl:for-each select=". | following-sibling::Class[position() &lt; 5]">
                <xsl:text>|</xsl:text>
                <xsl:value-of select="ClassID" />
            </xsl:for-each>
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>
Related