Re: [xsl] XSLT repetition constructs comparison

Subject: Re: [xsl] XSLT repetition constructs comparison
From: "Martin Honnen martin.honnen@xxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Thu, 14 Jan 2021 10:28:38 -0000
Am 14.01.2021 um 10:05 schrieb Mukul Gandhi mukulg@xxxxxxxxxxxxxxxxx:
Hi all,
 B  B I'm comparing the functionality of, XSLTB repetition constructs
xsl:for-each and xsl:iterate (versions 2.0 & 3.0). Below is my example
use case, and the corresponding XSLT solutions from my side,

XML input document:

<?xml version="1.0"?>
<students>
 B  B <student>
 B  B  B  <rollNo>1</rollNo>
 B  B  B  <fName>Sharon</fName>
 B  B  B  <lName>Adler</lName>
 B  B </student>
 B  B <student>
 B  B  B  <rollNo>2</rollNo>
 B  B  B  <fName>Anders</fName>
 B  B  B  <lName>Berglund</lName>
 B  B </student>
 B  B <student>
 B  B  B  <rollNo>3</rollNo>
 B  B  B  <fName>Norm</fName>
 B  B  B  <lName>Walsh</lName>
 B  B </student>
 B  B <student>
 B  B  B  <rollNo>4</rollNo>
 B  B  B  <fName>Michael</fName>
 B  B  B  <lName>Sperberg-McQueen</lName>
 B  B </student>
 B  B <student>
 B  B  B  <rollNo>5</rollNo>
 B  B  B  <fName>Florent</fName>
 B  B  B  <lName>Georges</lName>
 B  B </student>
</students>

I wish to transform, the above XML data into HTML, using XSLT. The
resulting HTML, needs to have a table containing rows representing each
XML input "student" element, and a total record count at the bottom of
HTML output.

Below are my various XSLT solutions,

(1) An XSLT 2.0 solution, with sorting (sorting by fName & lName):

<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform
<http://www.w3.org/1999/XSL/Transform>">

B B <xsl:output method="html" indent="yes"/>

 B  B <xsl:template match="students">
 B  B  B  <html>
 B  B  B  B  <head>
 B  B  B  B  B  B <title>Student list</title>
 B  B  B  B  </head>
 B  B  B  B  <body>
 B  B  B  B  B  B <table>
 B  B  B  B  B  B  B  <tr>
 B  B  B  B  B  B  B  B  B <td><b>Roll No.</b></td>
 B  B  B  B  B  B  B  B  B <td><b>First name</b></td>
 B  B  B  B  B  B  B  B  B <td><b>Last name</b></td>
 B  B  B  B  B  B  B  </tr>
 B  B  B  B  B  B  B  <xsl:for-each select="student">
 B  B  B  B  B  B  B  B  B <xsl:sort select="fName"/>
 B  B  B  B  B  B  B  B  B <xsl:sort select="lName"/>
 B  B  B  B  B  B  B  B  B <tr>
 B  B  B  B  B  B  B  B  B  B  <td align="center"><xsl:value-of
select="rollNo"/>.</td>
 B  B  B  B  B  B  B  B  B  B  <td><xsl:value-of select="fName"/></td>
 B  B  B  B  B  B  B  B  B  B  <td><xsl:value-of select="lName"/></td>
 B  B  B  B  B  B  B  B  B </tr>
 B  B  B  B  B  B  B  </xsl:for-each>
 B  B  B  B  B  B  B  <tr>
 B  B  B  B  <td colspan="3">&#xa0;</td>
 B  B  B  </tr>
 B  B  B  <tr>
 B  B  B  B  <td colspan="2"><b>Total no of students</b> : </td>
 B  B  B  B  <td><xsl:value-of select="count(student)"/></td>
 B  B  B  B  B  B  B  </tr>
 B  B  B  B  B  B </table>
 B  B  B  B  </body>
 B  B  B  </html>
 B  B </xsl:template>

</xsl:stylesheet>

(2)B An XSLT 3.0 solution, without sorting:

<?xml version="1.0"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform
<http://www.w3.org/1999/XSL/Transform>"

 B xmlns:xs="http://www.w3.org/2001/XMLSchema
<http://www.w3.org/2001/XMLSchema>"

B exclude-result-prefixes="xs">

B B <xsl:output method="html" indent="yes"/>

 B  B <xsl:template match="students">
 B  B  B  <html>
 B  B  B  B  <head>
 B  B  B  B  B  B <title>Student list</title>
 B  B  B  B  </head>
 B  B  B  B  <body>
 B  B  B  B  B  B <table>
 B  B  B  B  B  B  B  <tr>
 B  B  B  B  B  B  B  B  B <td><b>Roll No.</b></td>
 B  B  B  B  B  B  B  B  B <td><b>First name</b></td>
 B  B  B  B  B  B  B  B  B <td><b>Last name</b></td>
 B  B  B  B  B  B  B  </tr>
 B  B  B  B  B  B  B  <xsl:iterate select="student">
 B  B  B  B  B  B  B  B  B <xsl:param name="total" select="0"
as="xs:integer"/>
 B  B  B  B  B  B  B  B  B <xsl:on-completion>
 B  B  B  B  B  B  B  B  B  B  <tr>
 B  B  B  B  B  B  B  B  B  B  B  B <td colspan="3">&#xa0;</td>
 B  B  B  B  B  B  B  B  B  B  </tr>
 B  B  B  B  B  B  B  B  B  B  <tr>
 B  B  B  B  B  B  B  B  B  B  B  <td colspan="2"><b>Total no of
students</b> : </td>
 B  B  B  B  B  B  B  B  B  B  B  <td><xsl:value-of select="$total"/></td>
 B  B  B  B  B  B  B  B  B  B  </tr>
 B  B  B  B  B  B  B  B  B </xsl:on-completion>
 B  B  B  B  B  B  B  B  B <tr>
 B  B  B  B  B  B  B  B  B  B  <td align="center"><xsl:value-of
select="rollNo"/>.</td>
 B  B  B  B  B  B  B  B  B  B  <td><xsl:value-of select="fName"/></td>
 B  B  B  B  B  B  B  B  B  B  <td><xsl:value-of select="lName"/></td>
 B  B  B  B  B  B  B  B  B </tr>
 B  B  B  B  B  B  B  B  B <xsl:next-iteration>
 B  B  B  B  B  B  B  B  B  B  <xsl:with-param name="total" select="$total +
1"/>
 B  B  B  B  B  B  B  B  B </xsl:next-iteration>
 B  B  B  B  B  B  B  </xsl:iterate>
 B  B  B  B  B  B </table>
 B  B  B  B  </body>
 B  B  B  </html>
 B  B </xsl:template>

</xsl:stylesheet>

(3)B An XSLT 3.0 solution, with sorting:

<?xml version="1.0"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform
<http://www.w3.org/1999/XSL/Transform>"

 B xmlns:xs="http://www.w3.org/2001/XMLSchema
<http://www.w3.org/2001/XMLSchema>"

B exclude-result-prefixes="xs">

B B <xsl:output method="html" indent="yes"/>

 B  B <xsl:template match="students">
 B  B  B  <html>
 B  B  B  B  <head>
 B  B  B  B  B  B <title>Student list</title>
 B  B  B  B  </head>
 B  B  B  B  <body>
 B  B  B  B  B  B <table>
 B  B  B  B  B  B  B  <tr>
 B  B  B  B  B  B  B  B  B <td><b>Roll No.</b></td>
 B  B  B  B  B  B  B  B  B <td><b>First name</b></td>
 B  B  B  B  B  B  B  B  B <td><b>Last name</b></td>
 B  B  B  B  B  B  B  </tr>
 B  B  B  B  B  B  B  <xsl:variable name="result_1" as="element(tr)*">
 B  B  B  B  B  B  B  B  B <xsl:iterate select="student">
 B  B  B  B  B  B  B  B  B  B  <xsl:param name="total" select="0"
as="xs:integer"/>
 B  B  B  B  B  B  B  B  B  B  <xsl:on-completion>
 B  B  B  B  B  B  B  B  B  B  B  B <tr>
 B  B  B  B  B  B  B  B  B  B  B  B  B  <td colspan="3">&#xa0;</td>
 B  B  B  B  B  B  B  B  B  B  B  B </tr>
 B  B  B  B  B  B  B  B  B  B  B  B <tr>
 B  B  B  B  B  B  B  B  B  B  B  B  B <td colspan="2"><b>Total no of
students</b> :
</td>
 B  B  B  B  B  B  B  B  B  B  B  B  B <td><xsl:value-of
select="$total"/></td>
 B  B  B  B  B  B  B  B  B  B  B  B </tr>
 B  B  B  B  B  B  B  B  B  B  </xsl:on-completion>
 B  B  B  B  B  B  B  B  B  B  <tr>
 B  B  B  B  B  B  B  B  B  B  B  B <td align="center"><xsl:value-of
select="rollNo"/>.</td>
 B  B  B  B  B  B  B  B  B  B  B  B <td><xsl:value-of select="fName"/></td>
 B  B  B  B  B  B  B  B  B  B  B  B <td><xsl:value-of select="lName"/></td>
 B  B  B  B  B  B  B  B  B  B  </tr>
 B  B  B  B  B  B  B  B  B  B  <xsl:next-iteration>
 B  B  B  B  B  B  B  B  B  B  B  B <xsl:with-param name="total"
select="$total + 1"/>
 B  B  B  B  B  B  B  B  B  B  </xsl:next-iteration>
 B  B  B  B  B  B  B  B  B </xsl:iterate>
 B  B  B  B  B  B  B  </xsl:variable>
 B  B  B  B  B  B  B  <xsl:perform-sort select="$result_1[position() le
(count($result_1)-2)]">
 B  B  B  B  B  B  B  B  B <xsl:sort select="td[2]"/>
 B  B  B  B  B  B  B  B  B <xsl:sort select="td[3]"/>
 B  B  B  B  B  B  B  </xsl:perform-sort>
 B  B  B  B  B  B  B  <xsl:copy-of select="$result_1[position() gt
(count($result_1)-2)]"/>
 B  B  B  B  B  B </table>
 B  B  B  B  </body>
 B  B  B  </html>
 B  B </xsl:template>

</xsl:stylesheet>

I haven't mentioned the solution, where we could use xsl:apply-templates
instead of xsl:for-each or xsl:iterate.

Firstly, I find xsl:iterate much more functionally rich (except that it
doesn't provide native sorting support) than xsl:for-each, if there's a
requirement of XSLT sequential looping. If there's no requirement for
sorting, then in XSLT 3.0 environment, I'd opt to use xsl:iterate. If
there's requirement for sorting, then in XSLT 3.0 environment, I might
opt to use xsl:for-each instead of xsl:iterate (does anybody differ?).
When using XSLT 3.0 xsl:iterate, with a requirement of sorting, can
anyone suggest a different (and possibly better as well) solution than
(3) above?

I agree with Michael that you can always use fn:sort for the select expression of xsl:iterate in XSLT 3 or at least, in Saxon 9.8 and 9.9 HE where the higher-order variant of fn:sort is not available, roll your own sort function with xsl:function and xsl:perform sort that you then call in the select attribute of xsl:iterate.


In general, for that use case, I don't see why xsl:iterate is better than xsl:for-each. I would probably use push style processing with xsl:apply-templates.

Last, for the use with the variable, don't forget that
`xsl:perform-sort` can be directly applied to result constructed by
nested XSLT so you could also use

             <xsl:perform-sort>
                  <xsl:sort select="td[2]"/>
                  <xsl:sort select="td[3]"/>
                  <xsl:iterate select="student">
                  ...
                  </xsl:iterate>
             </xsl:perform-sort>

perhaps although I guess that will not work with the total column you
want to treat differently.

Current Thread