问题
Based on the following XML, what is the best way to achieve an alphanumeric sort in XSL?
Edit: to clarify, the XML below is just a simple sample the real XML would contain much more variant values.
<colors>
<item>
<label>Yellow 100</label>
</item>
<item>
<label>Blue 12</label>
</item>
<item>
<label>Orange 3</label>
</item>
<item>
<label>Yellow 10</label>
</item>
<item>
<label>Orange 26</label>
</item>
<item>
<label>Blue 117</label>
</item>
</colors>
E.g. I want a final outcome in this order:
Blue 12, Blue 117, Orange 3, Orange 26, Yellow 10, Yellow 100
This is "effectively" what I would want.
<xsl:apply-templates select="colors/item">
<xsl:sort select="label" data-type="text" order="ascending"/><!--1st sort-->
<xsl:sort select="label" data-type="number" order="ascending"/><!--2nd sort-->
</xsl:apply-templates>
<xsl:template match="item">
<xsl:value-of select="label"/>
<xsl:if test="position() != last()">,</xsl:if>
</xsl:template>
回答1:
Splitting the label text into text and number part using substring-before
and substring-after
will do in your example (however, this is not a general approach, but you get the idea):
<xsl:template match="/">
<xsl:apply-templates select="colors/item">
<xsl:sort select="substring-before(label, ' ')" data-type="text" order="ascending"/>
<!--1st sort-->
<xsl:sort select="substring-after(label, ' ')" data-type="number" order="ascending"/>
<!--2nd sort-->
</xsl:apply-templates>
</xsl:template>
<xsl:template match="item">
<xsl:value-of select="label"/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:template>
This gives the following output:
Blue 12, Blue 117, Orange 3, Orange 26, Yellow 10, Yellow 100
Update
A more generic way to solve your sorting problem would be to have the select
attribute of the xls:sort
element contain a string which is sortable according to the sort rules that you expect. E.g. in this string all numbers could be padded with leading 0's so that lexicgraphically sorting them as data-type="text"
will result in the correct alphanumeric order.
If you are using the XSLT engine of .NET you could use a simple extension function in C# to pad numbers with leading 0's:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:myExt="urn:myExtension"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl myExt">
<xsl:output method="xml" indent="yes" />
<msxsl:script language="C#" implements-prefix="myExt">
<![CDATA[
private static string PadMatch(Match match)
{
// pad numbers with zeros to a maximum length of the largest int value
int maxLength = int.MaxValue.ToString().Length;
return match.Value.PadLeft(maxLength, '0');
}
public string padNumbers(string text)
{
return System.Text.RegularExpressions.Regex.Replace(text, "[0-9]+", new System.Text.RegularExpressions.MatchEvaluator(PadMatch));
}
]]>
</msxsl:script>
<xsl:template match="/">
<sorted>
<xsl:apply-templates select="colors/item">
<xsl:sort select="myExt:padNumbers(label)" data-type="text" order="ascending"/>
</xsl:apply-templates>
</sorted>
</xsl:template>
<xsl:template match="item">
<xsl:value-of select="label"/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:template>
</xsl:stylesheet>
来源:https://stackoverflow.com/questions/777930/how-to-apply-an-alphanumeric-sort-in-xslt