Re: XSLT repetition constructs comparison

"Martin Honnen [email protected]" <[email protected]> Thu, 14 Jan 2021 10:28:47 -0000
Newsgroups gmane.text.xml.xsl.general.mulberrytech
Message-ID <[email protected]>
Am 14.01.2021 um 10:05 schrieb Mukul Gandhi [email protected]:
> Hi all,
>     I'm comparing the functionality of, XSLT 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>
>     <student>
>        <rollNo>1</rollNo>
>        <fName>Sharon</fName>
>        <lName>Adler</lName>
>     </student>
>     <student>
>        <rollNo>2</rollNo>
>        <fName>Anders</fName>
>        <lName>Berglund</lName>
>     </student>
>     <student>
>        <rollNo>3</rollNo>
>        <fName>Norm</fName>
>        <lName>Walsh</lName>
>     </student>
>     <student>
>        <rollNo>4</rollNo>
>        <fName>Michael</fName>
>        <lName>Sperberg-McQueen</lName>
>     </student>
>     <student>
>        <rollNo>5</rollNo>
>        <fName>Florent</fName>
>        <lName>Georges</lName>
>     </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>">
> 
>     <xsl:output method="html" indent="yes"/>
> 
>     <xsl:template match="students">
>        <html>
>          <head>
>             <title>Student list</title>
>          </head>
>          <body>
>             <table>
>                <tr>
>                   <td><b>Roll No.</b></td>
>                   <td><b>First name</b></td>
>                   <td><b>Last name</b></td>
>                </tr>
>                <xsl:for-each select="student">
>                   <xsl:sort select="fName"/>
>                   <xsl:sort select="lName"/>
>                   <tr>
>                      <td align="center"><xsl:value-of 
> select="rollNo"/>.</td>
>                      <td><xsl:value-of select="fName"/></td>
>                      <td><xsl:value-of select="lName"/></td>
>                   </tr>
>                </xsl:for-each>
>                <tr>
>          <td colspan="3">&#xa0;</td>
>        </tr>
>        <tr>
>          <td colspan="2"><b>Total no of students</b> : </td>
>          <td><xsl:value-of select="count(student)"/></td>
>                </tr>
>             </table>
>          </body>
>        </html>
>     </xsl:template>
> 
> </xsl:stylesheet>
> 
> (2) 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>"
>                                                
>   xmlns:xs="http://www.w3.org/2001/XMLSchema 
> <http://www.w3.org/2001/XMLSchema>"
>                                                
>   exclude-result-prefixes="xs">
> 
>     <xsl:output method="html" indent="yes"/>
> 
>     <xsl:template match="students">
>        <html>
>          <head>
>             <title>Student list</title>
>          </head>
>          <body>
>             <table>
>                <tr>
>                   <td><b>Roll No.</b></td>
>                   <td><b>First name</b></td>
>                   <td><b>Last name</b></td>
>                </tr>
>                <xsl:iterate select="student">
>                   <xsl:param name="total" select="0" as="xs:integer"/>
>                   <xsl:on-completion>
>                      <tr>
>                         <td colspan="3">&#xa0;</td>
>                      </tr>
>                      <tr>
>                        <td colspan="2"><b>Total no of students</b> : </td>
>                        <td><xsl:value-of select="$total"/></td>
>                      </tr>
>                   </xsl:on-completion>
>                   <tr>
>                      <td align="center"><xsl:value-of 
> select="rollNo"/>.</td>
>                      <td><xsl:value-of select="fName"/></td>
>                      <td><xsl:value-of select="lName"/></td>
>                   </tr>
>                   <xsl:next-iteration>
>                      <xsl:with-param name="total" select="$total + 1"/>
>                   </xsl:next-iteration>
>                </xsl:iterate>
>             </table>
>          </body>
>        </html>
>     </xsl:template>
> 
> </xsl:stylesheet>
> 
> (3) 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>"
>                                                
>   xmlns:xs="http://www.w3.org/2001/XMLSchema 
> <http://www.w3.org/2001/XMLSchema>"
>                                                
>   exclude-result-prefixes="xs">
> 
>     <xsl:output method="html" indent="yes"/>
> 
>     <xsl:template match="students">
>        <html>
>          <head>
>             <title>Student list</title>
>          </head>
>          <body>
>             <table>
>                <tr>
>                   <td><b>Roll No.</b></td>
>                   <td><b>First name</b></td>
>                   <td><b>Last name</b></td>
>                </tr>
>                <xsl:variable name="result_1" as="element(tr)*">
>                   <xsl:iterate select="student">
>                      <xsl:param name="total" select="0" as="xs:integer"/>
>                      <xsl:on-completion>
>                         <tr>
>                            <td colspan="3">&#xa0;</td>
>                         </tr>
>                         <tr>
>                           <td colspan="2"><b>Total no of students</b> : 
> </td>
>                           <td><xsl:value-of select="$total"/></td>
>                         </tr>
>                      </xsl:on-completion>
>                      <tr>
>                         <td align="center"><xsl:value-of 
> select="rollNo"/>.</td>
>                         <td><xsl:value-of select="fName"/></td>
>                         <td><xsl:value-of select="lName"/></td>
>                      </tr>
>                      <xsl:next-iteration>
>                         <xsl:with-param name="total" select="$total + 1"/>
>                      </xsl:next-iteration>
>                   </xsl:iterate>
>                </xsl:variable>
>                <xsl:perform-sort select="$result_1[position() le 
> (count($result_1)-2)]">
>                   <xsl:sort select="td[2]"/>
>                   <xsl:sort select="td[3]"/>
>                </xsl:perform-sort>
>                <xsl:copy-of select="$result_1[position() gt 
> (count($result_1)-2)]"/>
>             </table>
>          </body>
>        </html>
>     </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.

--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/3329386
or by email: [email protected]
--~--