我遇到的问题是我需要对元素进行排序,具体取决于它们的值,其中包含以句点分隔的数字。我需要根据第一个时段之前的数字值,然后是第一个和第二个时段之间的数字等对元素进行排序。我不知道,这个层次结构有多深,这是最大的问题。
<?xml version="1.0" encoding="UTF-8"?>
<root>
<ROW>2.0.1</ROW>
<ROW>1.2</ROW>
<ROW>1.1.1</ROW>
<ROW>1.2.0</ROW>
<ROW>1</ROW>
</root>
结果应该是这样的:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<ROW>1</ROW>
<ROW>1.1.1</ROW>
<ROW>1.2</ROW>
<ROW>1.2.0</ROW>
<ROW>2.0.1</ROW>
</root>
这有可能吗?感谢任何帮助。
答案 0 :(得分:4)
有一个“简单”的答案,不使用任何扩展名:将行值拆分为chucks并对其进行排序。
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="ROW">
<xsl:sort select="substring-before(concat(., '.'), '.')" data-type="number"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="ROW">
<xsl:param name="prefix" select="''"/>
<xsl:choose>
<!-- end of recursion, there isn't any more ROW with more chucks -->
<xsl:when test=". = substring($prefix, 1, string-length($prefix)-1)">
<xsl:copy-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="chuck" select="substring-before(concat(substring-after(., $prefix), '.'), '.')"/>
<!-- this test is for grouping ROW with same prefix, to skip duplicates -->
<xsl:if test="not(preceding-sibling::ROW[starts-with(., concat($prefix, $chuck))])">
<xsl:variable name="new-prefix" select="concat($prefix, $chuck, '.')"/>
<xsl:apply-templates select="../ROW[starts-with(., $new-prefix) or . = concat($prefix, $chuck)]">
<xsl:sort select="substring-before(concat(substring-after(., $new-prefix), '.'), '.')" data-type="number"/>
<xsl:with-param name="prefix" select="$new-prefix"/>
</xsl:apply-templates>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
答案 1 :(得分:1)
实现此目的的唯一问题是处理不同文本长度的单个数字(在句点之间)(即按该顺序排序1.0,2.0和10.0)。如果各个数字的大小(比如n个数字)有一个上限,那么创建一个排序键,它是所有数字从零填充到n个数字的串联。对于n = 3,这导致
Row Key (string)
1 001
1.0 001000
1.0.1 001000001
1.1 001001
1.2.1 001002001
2.0.1 002000001
10.0.1 010000001
然后按键排序。如果您遇到XSLT 1.0,则必须使用EXSLT扩展函数来进行解析和密钥规范化。
答案 2 :(得分:1)
它并不漂亮,但您可以使用xalan:nodeset函数将数字“预处理”到具有Jim所描述的易于排序的表达式的节点集中。
此示例适用于Xalan 2.5.1:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xalan="http://xml.apache.org/xalan">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<root>
<!-- Create a sort node with a sort expression wrapping each ROW -->
<xsl:variable name="nodes">
<xsl:for-each select="/root/ROW">
<xsl:variable name="sort-string">
<xsl:call-template name="create-sort-string">
<xsl:with-param name="sort-string" select="text()" />
</xsl:call-template>
</xsl:variable>
<sort sort-by="{$sort-string}">
<xsl:copy-of select="." />
</sort>
</xsl:for-each>
</xsl:variable>
<!-- Now sort the sort nodes and copy out the ROW elements -->
<xsl:for-each select="xalan:nodeset($nodes)/sort">
<xsl:sort select="@sort-by" data-type="text" />
<xsl:copy-of select="*" />
</xsl:for-each>
</root>
</xsl:template>
<xsl:template name="create-sort-string">
<xsl:param name="sort-string" />
<!-- Biggest number at each level -->
<xsl:variable name="max-num" select="1000" />
<xsl:choose>
<xsl:when test="contains($sort-string, '.')">
<xsl:value-of select="$max-num + number(substring-before($sort-string, '.'))" />
<xsl:call-template name="create-sort-string">
<xsl:with-param name="sort-string" select="substring-after($sort-string, '.')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($max-num + number($sort-string), '0')" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
我个人认为编写扩展函数会更好,但我知道这并不总是一种选择。