Re: Concatenating a nodeset (set of attributes)
I realized after sending my answer that it contained various flaws. You will have to divide by larger numbers (powers of 100 or 1000) to cope with more child STRUC elements. But dividing by larger numbers cannot be done endlessly. I think the precision of real numbers usually ends around 1 divided by 2 billion. So you either can handle more depth or more children, not both to a reasonable extend. Next to this, number() and format-number() of an empty node-set returns 'NaN'. So, sorting on numbers might look smart, but is actually a lot more complicated...
But your format-number brings me to the following idea. Why not use it to construct a string that can be used to sort alphanumerically:
<xsl:sort select="concat(format-number(@pos, '000'), translate(format-number(STRUC/@pos, '000'), 'Na', '00'), translate(format-number(STRUC/STRUC/@pos, '000'), 'Na', '00'))" data-type="text" />
The 'NaN's are less problematic here. Just convert the characters to zeros and alphanumerical ordering will not be disturbed. This however still limits the nesting level to the amount of levels you anticipated. If you want to do better (in XSLT 1.0?), you will have to do two steps.
In the first step you add the string that is constructed with the select expression of the sort as an attribute to the top-most STRUC element, extending the sort-key as long as the nesting continues, and using a format-string with more zeros if more child elements have to be supported.
<xsl:attribute name="sort-key"> <xsl:value-of select="format-number(@pos, '000')" /> <!-- recurse over nested levels --> <xsl:apply-templates select="STRUC" mode="add-sort-key" /> </xsl:attribute>
<xsl:template match="STRUC/STRUC" mode="add-sort-key"> <xsl:value-of select="format-number(@pos, '000')" /> <xsl:apply-templates select="STRUC" mode="add-sort-key" /> </xsl:template>
In the second step you simply sort on that attribute:
<xsl:apply-templates select="STRUC" mode="remove-sort-key"> <xsl:sort select="@sort-key" data-type="number" /> </xsl:apply-templates>
(removing the attribute again if it disturbs further processing)
You could use the node-set() extension function that is supported by most parsers to perform the two passes in one stylesheet, if you like. Simply gather the result of the first pass in a variable and pass the variable as an argument to the node-set() function and pass the result to another apply-templates:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common">
<xsl:variable name="rtf"> <xsl:apply-templates select="STRUC" mode="add-sort-key" /> </xsl:variable>
<xsl:apply-templates select="exsl:node-set($rtf)/STRUC" mode="remove-sort-key"> <xsl:sort select="@sort-key" data-type="number" /> </xsl:apply-templates>
Note, I'm using the exslt namespace for lesser dependence on the actual parser.
Just a last remark. It would be much easier to solve this problem if the STRUC elements were nested as a tree. Something like:
<TOC> <STRUC pos="2"> <STRUC pos="1" /> <STRUC pos="12" /> </STRUC> <STRUC pos="3"> <STRUC pos="4" /> <STRUC pos="4"> <STRUC pos="1" /> </STRUC> </STRUC> </TOC>
PURCHASE STYLUS STUDIO ONLINE TODAY!
Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!
Download The World's Best XML IDE!
Accelerate XML development with our award-winning XML IDE - Download a free trial today!
Subscribe in XML format