p2p.wrox.com Forums

p2p.wrox.com Forums (http://p2p.wrox.com/index.php)
-   XSLT (http://p2p.wrox.com/forumdisplay.php?f=86)
-   -   preceding-sibling after sort (http://p2p.wrox.com/showthread.php?t=24003)

rushman January 17th, 2005 06:16 PM

preceding-sibling after sort
 
Hi!

I have a problem with xsl:sort. Let's say we have this input

=== XML input =====
<?xml version="1.0"?>
<items>
 <item>
  <name>something</name>
  <number>0089</number>
  <dept>management</dept>
  <subItem>subItem1</subItem>
  <subItem>subItem2</subItem>
  <subItem>subItem3</subItem>
 </item>
 <item>
  <name>anything</name>
  <number>0092</number>
  <dept>management</dept>
  <subItem>subItem4</subItem>
  <subItem>subItem5</subItem>
  <subItem>subItem6</subItem>
 </item>
</items>
==== END XML input =====

Now, the stylesheet

==== XSL Stylesheet ====

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="xml" indent="yes"/>
 <xsl:template match="items">
  <groups>
   <xsl:call-template name="itemTemplate"/>
  </groups>
 </xsl:template>
 <xsl:template name="itemTemplate">
  <xsl:for-each select="item">
   <xsl:sort data-type="text" order="ascending" select="name"/>
   <xsl:sort data-type="number" order="ascending" select="number"/>
   <group>
    <deptVal>
     <xsl:choose>
      <xsl:when test="dept=preceding-sibling::node()[1]/dept">
       <xsl:text>same</xsl:text>
      </xsl:when>
      <xsl:otherwise>
       <xsl:text>different</xsl:text>
      </xsl:otherwise>
     </xsl:choose>
    </deptVal>
    <xsl:copy-of select="."/>
   </group>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>
======= END XSL ==========

Here's my actual output

==== OUTPUT FROM TRANSFORMATION ===
<?xml version="1.0" encoding="UTF-16"?>
<groups>
 <group>
  <deptVal>same</deptVal>
  <item>
   <name>anything</name>
   <number>0092</number>
   <dept>management</dept>
   <subItem>subItem4</subItem>
   <subItem>subItem5</subItem>
   <subItem>subItem6</subItem>
  </item>
 </group>
 <group>
  <deptVal>different</deptVal>
  <item>
   <name>something</name>
   <number>0089</number>
   <dept>management</dept>
   <subItem>subItem1</subItem>
   <subItem>subItem2</subItem>
   <subItem>subItem3</subItem>
  </item>
 </group>
</groups>
=== END OUTPUT ====

My problem is that the nodes are output in the order I specified in the sort, but the evaluation of the preceding-sibling follows the order BEFORE the sort (original document order).

Is there a way to force the sort BEFORE evaluating preceding-sibling in a case like mine?

Here's my desired output:

=== DESIRED OUTPUT ===
<?xml version="1.0" encoding="UTF-16"?>
<groups>
 <group>
  <deptVal>different</deptVal>
  <item>
   <name>anything</name>
   <number>0092</number>
   <dept>management</dept>
   <subItem>subItem4</subItem>
   <subItem>subItem5</subItem>
   <subItem>subItem6</subItem>
  </item>
 </group>
 <group>
  <deptVal>same</deptVal>
  <item>
   <name>something</name>
   <number>0089</number>
   <dept>management</dept>
   <subItem>subItem1</subItem>
   <subItem>subItem2</subItem>
   <subItem>subItem3</subItem>
  </item>
 </group>
</groups>

=== END DESIRED OUTPUT ====

Thanks a lot everyone!

(I wonder how Mister Kay will resolve this one...)

Dijkstra's law on Programming and Inertia:

If you don't know what your program is supposed to do, don't try to write it.

mhkay January 17th, 2005 07:14 PM

In general to do sorting then grouping you need to do a two-phase transformation. You can do this using two stylesheets, or using a single stylesheet if you write the result of the first phase to a variable and then (assuming XSLT 1.0) convert this variable to a node-set using the xx:node-set() extension function. This means that after sorting you need to copy the sorted records to the intermediate tree using xsl:copy-of; the preceding-sibling axis on the copied nodes will reflect their position in the intermediate tree. Remember that axes always reflect relationships among the nodes in a tree: sorting only changes the processing order of the nodes, it doesn't modify the original tree.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference

rushman January 18th, 2005 10:35 AM

Hi Mr. Kay!

I had the same idea yesterday when I was coming home from work. Your reply confirms that I'm on the right path.

Your answer raise another question for me. Is it possible with XSLT 1.0 and without extensions to create the intermediate tree? (I honestly haven't tried yet, but I'm going to right away)

Thanks again for your time.

mhkay January 18th, 2005 11:08 AM

Creating the intermediate tree is no problem in XSLT 1.0. But the resulting type is "result tree fragment" and 1.0 has severe restrictions on how you can process an RTF. This is why the xx:node-set() extension was invented, to "convert" the RTF to an ordinary node-set (the root node of a tree).

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference


All times are GMT -4. The time now is 01:17 AM.

Powered by vBulletin®
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.