1

I have a problem where I have an xml content with different keys needs to be grouped together and displayed, when displayed there is a pagination so I can't display all entries on the same page, I'm using for-each-group for grouping the items, and loop through each current-group() and display its item, the xml is something like this:

<entry>
    <key>key1</key>
    <value>value1</value>
</entry>

<entry>
    <key>key2</key>
    <value>value2</value>
</entry>

<entry>
    <key>key1</key>
    <value>value3</value>
</entry>
<entry>
    <key>key1</key>
    <value>value4</value>
</entry>
<entry>
    <key>key3</key>
    <value>value5</value>
</entry>
<entry>
    <key>key2</key>
    <value>value6</value>
</entry>
<entry>
    <key>key4</key>
    <value>value7</value>
</entry>
<entry>
    <key>key3</key>
    <value>value8</value>
</entry>
<entry>
    <key>key5</key>
    <value>value9</value>
</entry>
<entry>
    <key>key1</key>
    <value>value10</value>
</entry>
<entry>
    <key>key2</key>
    <value>value11</value>
</entry>

which should display like:

Page1:

key1
    value1
    value3
    value4
    value10

key2 
    value2
    value6
    value11
key3
    value5
    value8
key4
    value7

Page2:

 key5
    value9

the xsl I'm using without pagination is

<xsl:for-each-group select="entry" group-by="key">
    <xsl:value-of select="current-grouping-key()"/>
    <xsl:for-each select="current-grouping()">
           <xsl:apply-templates select="." />
      </xsl:for-each>
</xsl:for-each-group>

I need to display only 10 items on each page, how can I get the count of preceding entries of current-grouping()

edit for more info:

I have a variable $size and $offsite, what I'm basically trying to do is something like :

<xsl:if test="$count &gt; $offset and $count &lt;= $offset + $size">
    <xsl:apply-templates select="." />
</xsl:if>

where $count would have the count of all items already rendered, ideally if some how I can create a counter similar to position() in for-each but it doesn't restart with each loop of current-group().

if I don't want group items by key, I would have done something like this:

<xsl:for-each select="entry">
        <xsl:if test="position() &gt; $offset and position() &lt;= $offset + $size">
               <xsl:apply-templates select="." />
        </xsl:if>
</xsl:for-each>

but now since I'm using for-each-group, and the entries of each group are looped through individually, how can I keep track of entries that are already rendered from preceding groups.

Shadi
  • 557
  • 1
  • 6
  • 15
  • Please show us an example of more than 10 entries and the expected resulting code of transforming that example. – michael.hor257k Mar 12 '15 at 08:38
  • I updated the example to have more than 10 entries – Shadi Mar 12 '15 at 09:03
  • Actually, show a _complete_ input XML document, and a _complete_ stylesheet: http://stackoverflow.com/help/mcve. – Mathias Müller Mar 12 '15 at 10:13
  • The complete styelsheet is more than 1000 lines, and the xml is much more than that, I just added the related part, is there anything related I can add here that can help? why is the info I added is not enough ? – Shadi Mar 12 '15 at 10:33
  • What is your output format, HTML, some other XML format (XSL-FO?), plain text? – Martin Honnen Mar 12 '15 at 10:53
  • Html, each entry is a div and the key is a header, its something similar to a search results page – Shadi Mar 12 '15 at 10:58
  • @MartinHonnen, I added more details in the original question – Shadi Mar 12 '15 at 11:05
  • 2
    There is no "pagination" as such in XSLT or in HTML, so this part of your question is not clear. – michael.hor257k Mar 12 '15 at 11:18
  • I don't see your code create any `div` or other HTML elements. And it is not clear what you consider a page? Are you trying to print that HTML generated by your XSLT and want to use CSS with properties like `page-break-before`? Or do you want to create several documents and an index document that allows accessing the different "pages"? – Martin Honnen Mar 12 '15 at 11:20
  • $offset is passed to xsl depending on a request parameter, depending on the value of the parameter the entries will render, if its 0 then first 10 will render, if its 1 then the next 10, I didn't include the html initially since I was trying keep it minimal, the div is added inside for each element, and I didn't include the header since I didn't think it would make any difference to the question – Shadi Mar 12 '15 at 11:46
  • I edited the question again and added more details, please let me know if its still not clear. – Shadi Mar 12 '15 at 11:57
  • 1
    If you are applying the XSLT on the server with each request and you only want to return a certain part of the grouped elements then I would first store the grouped items in a variable and then extract the 10 items you want to return for a "page" requested as needed. A variable in XSLT 2.0 can easily store XML or HTML so it should not be that difficult to adapt your code returning 10 elements from a `for-each` to return 10 elements stored in a variable of grouped elements. – Martin Honnen Mar 12 '15 at 12:08

1 Answers1

2

You could do it in two stages - first re-order the whole unpaginated list of entries so they are grouped together by key (i.e. in the order that the final output requires), then take the appropriate page "slice" of that sequence to do the actual transformation:

<xsl:variable name="orderedEntries" as="element()*">
  <xsl:for-each-group select="entry" group-by="key">
    <xsl:sequence select="current-group()" />
  </xsl:for-each-group>
</xsl:variable>

<xsl:for-each-group select="$orderedEntries[
            position() &gt; $offset and position() &lt;= $offset + $size]"
     group-by="key">
  <xsl:value-of select="current-grouping-key()"/>
  <xsl:for-each select="current-group()">
    <xsl:apply-templates select="." />
  </xsl:for-each>
</xsl:for-each-group>

This will work even if the page break is part way through a group, e.g. an $offset and $size of 5 given your example input would produce

key2 
    value6
    value11
key3
    value5
    value8
key4
    value7

P.S. as Martin points out in a comment, the

  <xsl:for-each select="current-group()">
    <xsl:apply-templates select="." />
  </xsl:for-each>

is for most purposes equivalent to simply

  <xsl:apply-templates select="current-group()"/>

(the only difference being in the values of position() and last() that the called templates will see)

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
  • 1
    The ` ` should be ``, no? – Martin Honnen Mar 12 '15 at 12:15
  • @MartinHonnen it could be, yes, I just copied the OP's code (I'm just changing what their `for-each-group` selects, not what it does with the nodes it has selected). Though I have corrected `current-grouping()` to `current-group()`, thanks for pointing that bit out. – Ian Roberts Mar 12 '15 at 12:16
  • It might be clearer to use `subsequence($orderedEntries,$offset+1,$size)` as the `select` of the `xsl:for-each-group`. – Daniel Haley Mar 12 '15 at 22:48