Hi,

I have been trying to use multiple sorts on a long xml file using XSLT 2.0, which each one of the xml elements look like:

<song>
  <title>(I Just) Died In Your Arms Tonight</title>
  <category>Rock</category>
  <album>80 Popular Hits</album>
  <artist>Cutting Crew</artist>
  <date added="02-03-2009"/>
</song>

This is my XSL snippet, and I want to sort it by title,then category and then album.

<xsl:template match="song">
             <xsl:apply-templates>
              <xsl:sort select="title"/>
              <xsl:sort select="category"/>
              <xsl:sort select="artist"/>
         </xsl:apply-templates>                                
    </xsl:template>

Yet, I don't see any sorting going on, and not even title. What have I done wrong here?
Thanks for your help.

Recommended Answers

All 5 Replies

Can you provide a little more about what you're desired output is? I assume your input document has multiple <song> element nodes. There's nothing wrong with that code per say, but I don't know what you're trying to output. Maybe post a simple sample output?

Ah well, I'll post what I have now and if it doesn't meet your needs, we can adjust.

The xsl:sort command is a bit confusing. What you are actually telling the processor to do when you use the xsl:sort is this: the order of nodes in the INPUT document should you process them in. It has nothing to do with what you want the output trees to look like. The basis idea is you are "re-ordering" the input nodes, so that when they are processed, they are processed in that order.

So in your example, if you want to sort by "title", "category" then "artist", you need to xsl:sort them when you process the "song" node. Once you've sorted them, you can do whatever you want on your template match="song" and the result will be in the pre-sorted order. For the example below, I've created a different input document based on yours.

Input Document

<songs>
	<song sortposition="5">
		<title>I Just Died In Your Arms Tonight</title>
		<category>Rock</category>
		<artist>Cutting Crew</artist>
	</song>
	<song sortposition="4">
		<title>Another song</title>
		<category>B</category>
		<artist>B</artist>
	</song>
	<song sortposition="2">
		<title>Another song</title>
		<category>B</category>
		<artist>A</artist>
	</song>
	<song sortposition="3">
		<title>Another song</title>
		<category>A</category>
		<artist>B</artist>
	</song>
	<song sortposition="1">
		<title>Another song</title>
		<category>A</category>
		<artist>A</artist>
	</song>
</songs>

Notice I inserted a "sortposition" attribute that is ordered by by "title", "category" then "artist". This is order you WANT to process them in.

XSLT 2.0 Version

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:strip-space elements="*"/>
	<xsl:output method="xml" indent="yes"/>

	<xsl:template match="/">
		<songs>
			<xsl:apply-templates select="songs/song">
				<xsl:sort select="artist"/>
				<xsl:sort select="category"/>
				<xsl:sort select="artist"/>
			</xsl:apply-templates>
		</songs>
	</xsl:template>

        <!-- Anything can happen here -->
        <!-- I'm just copying the original input nodes -->
	<xsl:template match="song">
		<xsl:copy>
			<xsl:copy-of select="@sortposition"/>
			<xsl:apply-templates/>
		</xsl:copy>
	</xsl:template>

	<xsl:template match="title|category|artist">
		<xsl:copy-of select="."/>
	</xsl:template>
</xsl:stylesheet>

Notice how I'm doing the sorting on the "song" node. This means I'm pre-sorting the "song" node so that each node is processed by the sort order defined, from major to minor.

Output

<songs>
	<song sortposition="1">
		<title>Another song</title>
		<category>A</category>
		<artist>A</artist>
	</song>
	<song sortposition="2">
		<title>Another song</title>
		<category>B</category>
		<artist>A</artist>
	</song>
	<song sortposition="3">
		<title>Another song</title>
		<category>A</category>
		<artist>B</artist>
	</song>
	<song sortposition="4">
		<title>Another song</title>
		<category>B</category>
		<artist>B</artist>
	</song>
	<song sortposition="5">
		<title>I Just Died In Your Arms Tonight</title>
		<category>Rock</category>
		<artist>Cutting Crew</artist>
	</song>
</songs>

Hopefully this sheds some light on what you're trying to do. If not, we can revisit it!

Can you provide a little more about what you're desired output is? I assume your input document has multiple <song> element nodes. There's nothing wrong with that code per say, but I don't know what you're trying to output. Maybe post a simple sample output?

These are my first three songs:

<song>
  <title>(I Just) Died In Your Arms</title>
  <category>Rock</category>
  <album>80 Popular Hits</album>
  <artist>Cutting Crew</artist>
  <date added="03-24-2009"/>

</song>


<song>
  <title>Turning Japanese</title>
  <category>Rock</category>
  <album>80 Popular Hits</album>
  <artist>The Vapors</artist>
  <date added="04-13-2009"/>

</song>

<song>
  <title>Video Killed The Radio Star</title>
  <category>Rock</category>
  <album>80 Popular Hits</album>
  <artist>Erasure</artist>
  <date added="03-27-2009"/>

</song>

If I change and say I want artist to be the first one that does it sort, and then title and category, I get this:

(I Just) Died In Your Arms 80 Popular Hits Cutting Crew 03-24-2009
Turning Japanese 80 Popular Hits The Vapors 04-13-2009
Video Killed The Radio Star 80 Popular Hits Erasure 03-27-2009

These are not the songs that match the author. For some reason, I am not sure how this happened. Does this give you a better understanding of my problem?

Thanks for your help.

Maybe you posted before you saw my full reply, but my solution seems to solve your problem. You can't sort the children nodes inside the "song" template match and get what you want. You have to sort them before you process the "song" nodes.

Let me know.

Maybe you posted before you saw my full reply, but my solution seems to solve your problem. You can't sort the children nodes inside the "song" template match and get what you want. You have to sort them before you process the "song" nodes.

Let me know.

Thanks for your help. I got the "children" ordered in the way I wanted now, and I don't think I needed the sort position, the code seemed to be able to figure it out on its own.

However, I do have another problem now that everything is printed out correctly, yet the css and other things that I have declared in my xsl:template when it is / is not being fetching anymore. I even cut the sort part out and have a template specifically for songs that has all the html layout, css and all that. Yet, it is now only giving me "plain" text.

In my case, where can I put all this now? If I store apply-templates for the sort in the beginning of my xsl document like in the following

<xsl:template match="/">

      <xsl:apply-templates select="music_songs/song">
      <xsl:sort select="artist"/>
      <xsl:sort select="category"/>
      <xsl:sort select="artist"/>
     </xsl:apply-templates>    
    </xsl:template>

Where can I put the html stuff? This looks like something like this now:

<xsl:template match="music_songs">
     
        <html>
            <head>
                <title>My Page</title>               
                <link rel="stylesheet" href="css/cd_collection.css" type="text/css"/>
             </head>
            <body>
              <xsl:apply-templates/>
           </body>
          </html>

Have I missed something here?
Thanks for your help.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.