Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 6101961
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 23, 20262026-05-23T13:33:34+00:00 2026-05-23T13:33:34+00:00

This a very similar question as XSL: Transforming xml into a sorted multicolumn html

  • 0

This a very similar question as XSL: Transforming xml into a sorted multicolumn html table

But (unfortunately) there’s an extra requirement: it should be XSLT 1.0 without extension functions, i.e. without using the node-set function.

This is my simplified XML:

<demo>
  <config n_columns="3" />
  <messages>
    <msg date="2011-07-06" title="2nd message" />
    <title>message list</title>
    <msg date="2011-07-05" title="4th message" />
    <msg date="2011-07-06" title="3rd message" />
    <msg date="2011-07-07" title="1st message" />
  </messages>
</demo>

Using this stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" />
  <xsl:template match="/">
    <xsl:apply-templates select="demo/messages">
      <xsl:with-param name="n_columns" select="number(/demo/config/@n_columns)" />
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="messages">
    <xsl:param name="n_columns" />
    <div>
      <xsl:value-of select="concat(./title, ' (', $n_columns, ' columns)')" />
    </div>
    <table>
      <xsl:variable name="cells" select="msg" />
      <xsl:apply-templates select="$cells[(position() - 1) mod $n_columns = 0]"
        mode="row">
        <xsl:with-param name="n_columns" select="$n_columns" />
        <xsl:with-param name="cells" select="$cells" />
      </xsl:apply-templates>
    </table>
  </xsl:template>
  <xsl:template match="msg" mode="row">
    <xsl:param name="n_columns" />
    <xsl:param name="cells" />
    <xsl:variable name="n_row" select="position()" />
    <xsl:variable name="row_cells"
      select="$cells[position() > ($n_row - 1) * $n_columns][position() &lt;= $n_columns]" />
    <tr>
      <xsl:apply-templates select="$row_cells" mode="cell" />
      <xsl:call-template name="empty-cells">
        <xsl:with-param name="n" select="$n_columns - count($row_cells)" />
      </xsl:call-template>
    </tr>
  </xsl:template>
  <xsl:template match="msg" mode="cell">
    <td>
      <xsl:value-of select="@title" />
    </td>
  </xsl:template>
  <xsl:template name="empty-cells">
    <xsl:param name="n" />
    <xsl:if test="$n > 0">
      <td>
        <xsl:attribute name="colspan">
          <xsl:value-of select="$n" />
        </xsl:attribute>
        <xsl:text>&#xA0;</xsl:text>
      </td>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

Produces this HTML fragment as output:

<div>message list (3 columns)</div>
<table>
  <tr>
    <td>2nd message</td>
    <td>4th message</td>
    <td>3rd message</td>
  </tr>
  <tr>
    <td>1st message</td>
    <td colspan="2">&nbsp;</td>
  </tr>
</table>

What is obviously missing is the sort part…

Redefining the “cells” variable as follows is actually what I need:

<xsl:variable name="cells">
  <xsl:for-each select="msg">
    <xsl:sort select="@date" order="descending" />
    <xsl:sort select="@title" />
    <xsl:copy-of select="." />
  </xsl:for-each>
</xsl:variable>

But now I must define another variable to convert the RTF to a nodelist and pass that one to the template I’m applying.

<xsl:variable name="sCells" select="ext:node-set($cells)/*" />

Doing that would produce the following HTML fragment:

<div>message list (3 columns)</div>
<table>
  <tr>
    <td>1st message</td>
    <td>2nd message</td>
    <td>3rd message</td>
  </tr>
  <tr>
    <td>4th message</td>
    <td colspan="2">&nbsp;</td>
  </tr>
</table>

Unfortunately, my XSLT engine (SAP XML toolkit for java) doesn’t support this (or a similar) extension function. Thus I’m looking for another solution that doesn’t require the node-set extension function.

I spent quite some time reading all kinds of forums etc., but I really can’t figure it out. Perhaps someone has a good idea for an alternative approach? tnx!


This is the follow up based on Dimitre’s (slightly extended) solution.
This XML input

<demo>
  <config n_columns="3" />
  <messages>
    <msg date="2011-07-06" title="2nd message" />
    <title>message list</title>
    <msg date="2011-07-05" title="4th message" />
    <msg date="2011-07-06" title="3rd message" />
    <msg date="2011-07-07" title="1st message" />
    <msg date="2011-07-05" title="5th message" />
    <msg date="2011-07-05" title="7th message" />
    <msg date="2011-07-05" title="6th message" />
  </messages>
</demo>

combined with this XSLT stylesheet

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

  <xsl:variable name="vNumCols" select="/*/config/@n_columns" />
  <xsl:variable name="vCells" select="/*/messages/msg" />
  <xsl:variable name="vNumCells" select="count($vCells)" />
  <xsl:variable name="vNumRows" select="ceiling($vNumCells div $vNumCols)" />
  <xsl:variable name="vIndexPatternLength"
    select="string-length(concat('', $vNumCells))" />
  <xsl:variable name="vIndexPattern">
    <xsl:call-template name="padding">
      <xsl:with-param name="length" select="$vIndexPatternLength" />
      <xsl:with-param name="chars" select="'0'" />
    </xsl:call-template>
  </xsl:variable>
  <xsl:variable name="vSortedIndex">
    <xsl:for-each select="$vCells">
      <xsl:sort select="@date" order="descending" />
      <xsl:sort select="@title" />
      <xsl:value-of
        select="format-number(count(preceding-sibling::msg) + 1,
          $vIndexPattern)" />
    </xsl:for-each>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:apply-templates select="demo/messages" />
  </xsl:template>
  <xsl:template match="messages">
    <table>
      <xsl:for-each select="$vCells[not(position() > $vNumRows)]">
        <xsl:variable name="vRow" select="position()" />
        <tr>
          <xsl:for-each select="$vCells[not(position() > $vNumCols)]">
            <xsl:variable name="vCol" select="position()" />
            <xsl:variable name="vCell"
              select="($vRow - 1) * $vNumCols + $vCol" />
            <xsl:variable name="vIndex"
              select="substring($vSortedIndex,
                ($vCell - 1) * $vIndexPatternLength + 1,
                $vIndexPatternLength)" />
            <xsl:variable name="vMessage"
              select="$vCells[position() = $vIndex]" />
            <xsl:choose>
              <xsl:when test="$vMessage">
                <xsl:apply-templates select="$vMessage"
                  mode="cell" />
              </xsl:when>
              <xsl:otherwise>
                <xsl:call-template name="empty-cell" />
              </xsl:otherwise>
            </xsl:choose>
          </xsl:for-each>
        </tr>
      </xsl:for-each>
    </table>
  </xsl:template>

  <xsl:template match="msg" mode="cell">
    <td>
      <xsl:apply-templates select="." />
    </td>
  </xsl:template>
  <xsl:template match="msg">
    <xsl:value-of select="concat(@date, ' : ', @title)" />
  </xsl:template>

  <xsl:template name="empty-cell">
    <td>
      <xsl:text>&#xA0;</xsl:text>
    </td>
  </xsl:template>

  <!-- http://www.exslt.org/str/functions/padding/ -->
  <xsl:template name="padding">
    <xsl:param name="length" select="0" />
    <xsl:param name="chars" select="' '" />
    <xsl:choose>
      <xsl:when test="not($length) or not($chars)" />
      <xsl:otherwise>
        <xsl:variable name="string"
          select="concat($chars, $chars, $chars, $chars, $chars, 
                         $chars, $chars, $chars, $chars, $chars)" />
        <xsl:choose>
          <xsl:when test="string-length($string) >= $length">
            <xsl:value-of select="substring($string, 1, $length)" />
          </xsl:when>
          <xsl:otherwise>
            <xsl:call-template name="padding">
              <xsl:with-param name="length" select="$length" />
              <xsl:with-param name="chars" select="$string" />
            </xsl:call-template>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

produces this HTML output

<table>
  <tr>
    <td>2011-07-07 : 1st message</td>
    <td>2011-07-06 : 2nd message</td>
    <td>2011-07-06 : 3rd message</td>
  </tr>
  <tr>
    <td>2011-07-05 : 4th message</td>
    <td>2011-07-05 : 5th message</td>
    <td>2011-07-05 : 6th message</td>
  </tr>
  <tr>
    <td>2011-07-05 : 7th message</td>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
  </tr>
</table>

Thanks Dimitre!

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-23T13:33:35+00:00Added an answer on May 23, 2026 at 1:33 pm

    It is difficult, but not impossible to perform the required processing in XSLT 1.0 without using any extension functions:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:variable name="vNumCols"
          select="/*/config/@n_columns"/>
    
     <xsl:variable name="vItems" select="/*/messages/msg"/>
    
     <xsl:variable name="vNumItems" select="count($vItems)"/>
    
     <xsl:variable name="vNumRows" select=
       "ceiling($vNumItems div $vNumCols)"/>
    
     <xsl:variable name="vsortedInds">
      <xsl:for-each select="$vItems">
       <xsl:sort select="@date" order="descending"/>
    
       <xsl:value-of select=
       "format-number(count(preceding-sibling::msg)+1,
                      '0000'
                      )
       "/>
      </xsl:for-each>
     </xsl:variable>
    
     <xsl:template match="/">
      <table>
       <xsl:for-each select=
       "$vItems[not(position() > $vNumRows)]">
         <tr>
           <xsl:variable name="vRow" select="position()"/>
    
           <xsl:for-each select="$vItems[not(position() > $vNumCols)]">
              <xsl:variable name="vcurIndIndex" select=
               "($vRow -1)*$vNumCols + position()"/>
              <xsl:variable name="vcurInd" select=
              "substring($vsortedInds, 4*($vcurIndIndex -1) +1, 4)"/>
    
              <xsl:variable name="vcurItem" select="$vItems[position()=$vcurInd]"/>
              <xsl:if test="$vcurItem">
               <td>
                <xsl:value-of select="$vcurItem/@title"/>
               </td>
              </xsl:if>
           </xsl:for-each>
         </tr>
       </xsl:for-each>
      </table>
     </xsl:template>
    </xsl:stylesheet>
    

    When this transformation is applied on the provided XML document:

    <demo>
        <config n_columns="3" />
        <messages>
            <msg date="2011-07-06" title="2nd message" />
            <title>message list</title>
            <msg date="2011-07-05" title="4th message" />
            <msg date="2011-07-06" title="3rd message" />
            <msg date="2011-07-07" title="1st message" />
        </messages>
    </demo>
    

    the significant part of the desired output is produced (I am leaving the rest as an exercise to the reader 🙂 ):

    <table>
       <tr>
          <td>1st message</td>
          <td>2nd message</td>
          <td>3rd message</td>
       </tr>
       <tr>
          <td>4th message</td>
       </tr>
    </table>
    

    Explanation:

    1. In order to avoid having to convert an RTF to a node-set, we are using a string of the indexes of the sorted elements. Every index occupies four characters (left padded with zeroes when necessary). Then we are using these indexes in populating the rows of the table.

    2. In order to avoid resursion, we are using the Piez method of iteration through N non-node items.

    Do note: This solution assumes that the table will not contain more than 9999 cells. If more cells are expected, you can easily change the code, for example:

    Replace:

    format-number(count(preceding-sibling::msg)+1,
                          '0000'
                        )
    

    with:

    format-number(count(preceding-sibling::msg)+1,
                          '00000'
                        )
    

    And replace:

              <xsl:variable name="vcurInd" select=
              "substring($vsortedInds, 4*($vcurIndIndex -1) +1, 4)"/>
    

    with

              <xsl:variable name="vcurInd" select=
              "substring($vsortedInds, 5*($vcurIndIndex -1) +1, 5)"/>
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

No related questions found

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.