2

I'm using

<xsl:sort />

to sort the rows in a table. The value of

@result 

can be Failed, Ignored or Passed. As it is sorted alphabetically it will not come out in the order I want. Which is

Failed - Ignored - Passed

How do I achieve this I am using xslt 1.0

This is my code

<xsl:apply-templates select="results/test-case">
<xsl:sort select="@result" /> 
</xsl:apply-templates>
3
  • can you provide xml sample ? Commented Nov 5, 2013 at 11:27
  • include the XML too.. Commented Nov 5, 2013 at 11:27
  • You say you want Failed, Ignored, Passed - that is alphabetical order. Commented Nov 5, 2013 at 11:32

3 Answers 3

9

In your question the ordering you've asked for is consistent with alphabetical order, so a plain <xsl:sort select="@result" /> should work correctly. But in cases where a non-alphabetic fixed ordering is required I tend to use the following trick.

First define the order in a variable, with entries delimited by some character that is not part of any of the options:

<xsl:variable name="sortOrder" select="'|Passed|Failed|Ignored|'" />

Then use

<xsl:sort data-type="number" select="string-length(
    substring-before($sortOrder, concat('|', @result, '|')))" />

The trick here is that substring-before($sortOrder, concat('|', @result, '|')) will be the empty string when @result is Passed, the string "|Passed" when @result is Failed, and the string "|Passed|Failed" when @result is Ignored. Thus sorting numerically by the length of these strings will produce the ordering given by $sortOrder.

In this particular case you don't even need the concat, just

<xsl:sort data-type="number" select="string-length(
    substring-before($sortOrder, @result))" />

will do the job. The concat is there in the general case to handle situations where one item in the ordering is a substring of another. For example, for an ordering of '|Passed|Pass|Failed|Fail|' the concat is required otherwise "Pass" would be treated the same as "Passed" rather than being consistently sorted after it.

Sign up to request clarification or add additional context in comments.

1 Comment

It's great to search stack overflow and find answers like this.
2

The following alternate XSLT is definitely not as cool as @IanRoberts's but it may be helpful in some setups when e.g. the sorting order is defined by another process and hence be available in another XML file.

The following file sort.xml is a simple example defining a sort order:

<?xml version="1.0" encoding="ISO-8859-1"?>
<sort_indices>
  <sort_index name="my_sort">
    <entry key="Failed" index="2"/>
    <entry key="Ignored" index="0"/>
    <entry key="Passed" index="1"/>
  </sort_index>
</sort_indices>

This file can be sourced into the XSLT using document and then used as sorting index:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />

  <xsl:variable name="sort_index" select="document('sort.xml')/sort_indices/sort_index[@name='my_sort']"/>

  <xsl:template match="/">

    <test-cases>
      <xsl:apply-templates select="results/test-case">
        <xsl:sort select="$sort_index/entry[@key = current()/@result]/@index"/> 
      </xsl:apply-templates>
    </test-cases>

  </xsl:template>

</xsl:stylesheet>

This way the sorting uses the numeric index values of the sort index directly without having to derive them from the length of an encoded string. It looks cleaner but as I said it's not as cool. :-)

3 Comments

Nice idea. It would be even nicer in XSLT 2.0 where you could define a key over the sort_index to do direct lookups from the entry key to index, but that would rely on the three-argument form of the key function so not possible in 1.0.
That's actually what I tried first but I had to learn that it's not possible to use the sub tree returned by document as context in the match attribute of <key>. :-)
In XSLT 1.0 the key function always looks up nodes in the document that the current context node belongs to. Therefore it is possible to look up nodes in another document but only by switching the context (e.g. with <xsl:for-each select="$sort_index">). But in an xsl:sort expression the context is always the thing you're trying to sort. XSLT 2.0 adds a third parameter to key that lets you look things up in a subtree rooted at an arbitrary node, which could belong to a document other than the current context one.
1

XSLT 2.0 has a more elegant way:

<xsl:sort select="index-of(('Passed', 'Failed', 'Ignored'), .) "/>

3 Comments

But this question is tagged as xslt-1.0.
So? The question was asked 7 years ago, and the XSLT 2.0 solution is more relevant today than it was in 2013. The original asker clearly doesn't need this input. This question and answer comes up in Google today. I could not find a 2.0 answer, eventually came up with this elegant solution. So posted it here to help other people in 2020 and later. I find most older questions with 1.0 solutions have 2.0 (and 3.0) answer added years later, and I always find that helpful.
"I could not find a 2.0 answer" You need better search methods: stackoverflow.com/a/32625442/3016153

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.