Sorting on a variable

Subject: Sorting on a variable
From: "John E. Simpson" <simpson@xxxxxxxxxxx>
Date: Sun, 15 Oct 2000 13:06:05 -0400
I've got XML which looks like this:

<products>
  <product prodID="A1234">
    <name>First prod</name>
    <price curr="USD">29.95</price>
  </product>
  <product prodID="A5678">
    <name>Second prod</name>
    <price curr="GBP">29.95</price>
  </product>
  <product prodID="A9012">
    <name>Third prod</name>
    <price curr="EU">29.95</price>
  </product>
  <product prodID="A9012">
    <name>Fourth prod</name>
    <price curr="USD">50.00</price>
  </product>
</products>

No problem at all displaying this as a table, sorted (or not) by name or price. But then I wanted to sort not by raw price, but by a single "USD equivalent" -- converting the price to a common denomination.

Here's a variable to do the conversion (rates as of last night :):

<xsl:variable name="usd_equiv">
  <xsl:choose>
    <xsl:when test="price/@curr='USD'">
      <xsl:value-of select="price"/>
    </xsl:when>
    <xsl:when test="price/@curr='GBP'">
      <xsl:value-of select="price * 1.47275"/>
    </xsl:when>
    <xsl:when test="price/@curr='EU'">
      <xsl:value-of select="price * 0.864379"/>
    </xsl:when>
    <xsl:otherwise>Unknown Currency</xsl:otherwise>
  </xsl:choose>
</xsl:variable>

(I've varied the values of the xsl:when test attributes in different ways; this is just the most current iteration, which assumes the context node to be a <product> element.)

Using a variable with xsl:sort introduces a number of stylesheet structural problems. E.g., if you do the sort within a for-each, the xsl:sort must be a first child of the xsl:for-each... and of course the variable goes out of scope for use *by* the xsl:sort. But if you do xsl:sort as a child of xsl:apply-templates, well, xsl:variable is not a legitimate child of xsl:apply-templates.

So then, rethinking a bit, I came up with what I thought was a fairly simple solution, a global variable:

<xsl:variable name="product_usd">
  <xsl:for-each select="/products/product">
    <xsl:copy-of select="."/>
    <usd_equiv>
      <xsl:choose>
        <xsl:when test="price/@curr='USD'">
          <xsl:value-of select="price"/>
        </xsl:when>
        <xsl:when test="price/@curr='GBP'">
          <xsl:value-of select="price * 1.47275"/>
        </xsl:when>
        <xsl:when test="price/@curr='EU'">
          <xsl:value-of select="price * 0.864379"/>
        </xsl:when>
        <xsl:otherwise>Unknown Currency</xsl:otherwise>
      </xsl:choose>
    </usd_equiv>
  </xsl:for-each>
</xsl:variable>

Basically, this clones the <product> elements from the source tree (with xsl:copy-of select=".") into a variable ($prod_usd) as an RTF. And -- I thought -- it added an extra child element, <usd_equiv>, as a child of each <product> element. Then in the xsl:sort, instead of sorting the source tree, I'm sorting (with the node-set() function converting this RTF to a true node-set) the RTF contained by this variable, with <usd_equiv> as my sort key.

Near as I can tell, this should be giving me *something*. The problem is that the <usd_equiv> element always seems to be empty.

I feel really stupid. Must be missing something obvious... and at this point, have been banging my head against the problem for, like, 6 hours.

Any ideas?

THANKS in advance!
===============================================================
John E. Simpson | "He asked me if I knew what
http://www.flixml.org | time it was. I said, 'Yes, but
XML Q&A: http://www.xml.com | not right now.'" (Steven Wright)



XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list



Current Thread