tags:

views:

210

answers:

2

I've already created my XSLT but id like to be able to sort the data, also add some kind of index so i can group the items together, the difficulty Im having is the the node i want to sort by contains multiple values - values id like to sort by.

For example here is my XML:

<item>
 <title>Item 1</title>
 <subjects>English,Maths,Science,</subjects>
 <description>Blah Blah Bah...</description>
</item>
<item>
 <title>Item 2</title>
 <subjects>Geography,Physical Education</subjects>
 <description>Blah Blah Bah...</description>
</item>
<item>
 <title>Item 3</title>
 <subjects>History, Technology</subjects>
 <description>Blah Blah Bah...</description>
</item>
<item>
 <title>Item 4</title>
 <subjects>Maths</subjects>
 <description>Blah Blah Bah...</description>
</item>

So if i sort by <subjects> I get this order:

English,Maths,Science,
Geography,Physical Education
History, Technology
Maths

But I would like this kind of output:

English
Geography
History
Maths
Maths
Physical Education
Science
Technology

Outputting the XML for each subject contained in <subjects>, so Item1 contains subjects Maths, English & Science so I want to output that Title and Description 3 times because its relevant to all 3 subjects.

Whats the best way in XSLT to do this?

+1  A: 

Well, processing the contents of text nodes isn't really the mandate of XSLT. If you can, you should probably change the representation to add some more XML structure into the subjects elements. Otherwise you'll have to write some really clever string processing code using XPath string functions, or perhaps use a Java-based XSLT processor and hand off the string processing to a Java method. It's not straightforward.

Peter Eisentraut
+1  A: 

I think one way to do this would be by using the node-set extenstion function to do multi-pass processing. Firstly you would loop through the existing subject nodes, splitting them by commas, to create a new set of item nodes; one per subject.

Next, you would loop through this new node set in subject order.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="urn:schemas-microsoft-com:xslt" extension-element-prefixes="exsl" version="1.0">

   <xsl:output method="text"/>

   <xsl:template match="/">
      <xsl:variable name="newitems">
         <xsl:for-each select="items/item">
            <xsl:call-template name="splititems">
               <xsl:with-param name="itemtext" select="subjects"/>
            </xsl:call-template>
         </xsl:for-each>
      </xsl:variable>
      <xsl:for-each select="exsl:node-set($newitems)/item">
         <xsl:sort select="text()"/>
         <xsl:value-of select="text()"/>
         <xsl:text> </xsl:text>
      </xsl:for-each>
   </xsl:template>

   <xsl:template name="splititems">
      <xsl:param name="itemtext"/>
      <xsl:choose>
         <xsl:when test="contains($itemtext, ',')">
            <item>
               <xsl:value-of select="substring-before($itemtext, ',')"/>
            </item>
            <xsl:call-template name="splititems">
               <xsl:with-param name="itemtext" select="substring-after($itemtext, ',')"/>
            </xsl:call-template>
         </xsl:when>
         <xsl:when test="string-length($itemtext) &gt; 0">
            <item>
               <xsl:value-of select="$itemtext"/>
            </item>
         </xsl:when>
      </xsl:choose>
   </xsl:template>

</xsl:stylesheet>

Note that the above example uses Microsoft's Extension functions. Depending on what XSLT processor you are using, you may have to specify another namespace for the processor.

You may also need to do some 'trimming' of the subjects, because in your XML sample above, there is a space before one of the subjects (Technology) in the comma-delimited list.

Tim C