我有一个排序数字的问题,用句点分隔(例如1,2,1.1,1.3)。我在这里找到了一个解决方案XSL recursive sort。这就是我需要的,略有不同。 在我的xml中,标签就像
<root>
<row>
<col name="rank"/>
<name>A</name>
<val>1.1</val>
</row>
<row>
<col name="rank"/>
<name>B</name>
<val>1</val>
</row>
<row>
<col name="level"/>
<name>C</name>
<val>test</val>
</row>
<row>
<col name="rank"/>
<name>D</name>
<val>1.2.2</val>
</row>
<row>
<col name="rank"/>
<name>E</name>
<val>1.2.1</val>
</row>
<row>
<col name="rank"/>
<name>F</name>
<val>1.2</val>
</row>
</root>
我希望根据“val”标签对col / @ name =“rank”的所有行进行排序。是否可以通过仅修改链接问题中的已接受答案来获取输出?如果没有,是否有任何xsl版本1的解决方案(如果没有,则为2)。 我需要的输出是:
<ul>
<li>1 - B
<ul>
<li>1.1 - A</li>
<li>1.2 - F
<ul>
<li>1.2.1 - E</li>
<li>1.2.2 - D</li>
</ul>
</li>
</ul>
</li>
</ul>
更新我:基于michael.hor257k的回答,这是我正在寻找的解决方案:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="5.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<html>
<body>
<ul>
<xsl:apply-templates select="row[not(contains(val, '.'))][contains(col/@name, 'rank')]">
<xsl:sort select="val" data-type="number" order="ascending"/>
</xsl:apply-templates>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="row">
<li>
<xsl:variable name="parent" select="concat(val, '.')" />
<xsl:value-of select="./name"/> - <xsl:value-of select="./val"/>
<xsl:if test="../row[starts-with(val, $parent)][not(contains(substring-after(val, $parent), '.'))][contains(col/@name, 'rank')]">
<ul>
<xsl:apply-templates select="../row[starts-with(val, $parent)][not(contains(substring-after(val, $parent), '.'))][contains(col/@name, 'rank')]">
<xsl:sort select="substring-after(val, $parent)" data-type="number" order="ascending"/>
</xsl:apply-templates>
</ul>
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>
更新II :感谢Dimitre Novatchev,我有一个更好的解决方案,我认为这是最好的答案。所以,我试图理解它,之后我会把它检查为已接受的答案。
更新III :我接受了michael.hor257k发布的答案,因为这是我需要的。我知道我的xml中的排名没有跳跃但是正如Dimitre Novatchev所提到的那样,如果有1.3.2没有1.3,那么这个解决方案会有问题,你可以使用Dimitre Novatchev发布的完整答案。
答案 0 :(得分:1)
已知级别数量?如果是这样,在XSLT 2.0中你可以使用
<xsl:apply-templates select="row[col/@name = 'rank']">
<xsl:sort select="xs:integer(tokenize(val, '\'.')[1])"/>
<xsl:sort select="xs:integer(tokenize(val, '\'.')[2])"/>
<xsl:sort select="xs:integer(tokenize(val, '\'.')[3])"/>
</xsl:apply-templates/>
三个级别。在XSLT 3.0中,您甚至可以使用任何级别的sort
函数来执行此操作:<xsl:apply-templates select="sort(row[col/@name = 'rank'], function($row) { tokenize($row/val, '\.')!xs:integer(.) })">
尽管您还希望嵌套我认为使用递归函数执行
<xsl:for-each-group select="$rows" group-by="xs:integer(tokenize(val, '\.')[1])"><xsl:sort select="current-grouping-key()"/>...</xsl:for-each-group>
更适合XSLT 2.0或3.0然后纯排序。
完整的样式表
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf" version="2.0">
<xsl:output method="html" indent="yes"/>
<xsl:function name="mf:nest" as="element()*">
<xsl:param name="rows" as="element(row)*"/>
<xsl:sequence select="mf:nest($rows, 1)"/>
</xsl:function>
<xsl:function name="mf:nest" as="element()*">
<xsl:param name="rows" as="element(row)*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:for-each-group select="$rows" group-by="xs:integer(tokenize(val, '\.')[$level])">
<xsl:sort select="current-grouping-key()"/>
<li>
<xsl:variable name="item" select="current-group()[not(tokenize(val, '\.')[$level + 1])]"/>
<xsl:value-of select="$item/concat(name, ' - ', val)"/>
<xsl:if test="current-group()[2]">
<ul>
<xsl:sequence select="mf:nest(current-group() except $item, $level + 1)"/>
</ul>
</xsl:if>
</li>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="root">
<ul>
<xsl:sequence select="mf:nest(row[col/@name = 'rank'])"/>
</ul>
</xsl:template>
</xsl:stylesheet>
它转换输入
<root>
<row>
<col name="rank"/>
<name>A</name>
<val>1.1</val>
</row>
<row>
<col name="rank"/>
<name>B</name>
<val>1</val>
</row>
<row>
<col name="level"/>
<name>C</name>
<val>test</val>
</row>
<row>
<col name="rank"/>
<name>D</name>
<val>1.2.2</val>
</row>
<row>
<col name="rank"/>
<name>E</name>
<val>1.2.1</val>
</row>
<row>
<col name="rank"/>
<name>foo</name>
<val>2</val>
</row>
<row>
<col name="rank"/>
<name>bar</name>
<val>1.10</val>
</row>
<row>
<col name="rank"/>
<name>F</name>
<val>1.2</val>
</row>
<row>
<col name="rank"/>
<name>F</name>
<val>1.10.1</val>
</row>
</root>
进入结果
<ul>
<li>B - 1
<ul>
<li>A - 1.1</li>
<li>F - 1.2
<ul>
<li>E - 1.2.1</li>
<li>D - 1.2.2</li>
</ul>
</li>
<li>bar - 1.10
<ul>
<li>F - 1.10.1</li>
</ul>
</li>
</ul>
</li>
<li>foo - 2</li>
</ul>
答案 1 :(得分:1)
考虑以下样式表:
XSLT 1.0
<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:template match="/root">
<list>
<xsl:apply-templates select="row[not(contains(val, '.'))]">
<xsl:sort select="val" data-type="number" order="ascending"/>
</xsl:apply-templates>
</list>
</xsl:template>
<xsl:template match="row">
<xsl:variable name="parent" select="concat(val, '.')" />
<item val="{val}">
<xsl:apply-templates select="../row[starts-with(val, $parent)][not(contains(substring-after(val, $parent), '.'))]">
<xsl:sort select="substring-after(val, $parent)" data-type="number" order="ascending"/>
</xsl:apply-templates>
</item>
</xsl:template>
</xsl:stylesheet>
应用于以下输入示例:
<强> XML 强>
<root>
<row>
<col name="rank"/>
<name>A</name>
<val>1.1</val>
</row>
<row>
<col name="rank"/>
<name>B</name>
<val>1</val>
</row>
<row>
<col name="rank"/>
<name>F</name>
<val>1.10</val>
</row>
<row>
<col name="level"/>
<name>C</name>
<val>2</val>
</row>
<row>
<col name="rank"/>
<name>D</name>
<val>1.2.2</val>
</row>
<row>
<col name="rank"/>
<name>E</name>
<val>1.2.1</val>
</row>
<row>
<col name="rank"/>
<name>F</name>
<val>1.2</val>
</row>
</root>
结果将是:
<?xml version="1.0" encoding="UTF-8"?>
<list>
<item val="1">
<item val="1.1"/>
<item val="1.2">
<item val="1.2.1"/>
<item val="1.2.2"/>
</item>
<item val="1.10"/>
</item>
<item val="2"/>
</list>
这是递归工作的,并且对级别数没有限制。但请注意,每个项目(&#34;祖先&#34;不包含点的项目除外)必须有父项。
答案 2 :(得分:0)
我遇到排序数字的问题,以句点分隔(例如1, 2.1,1.1,1.3)。我在这里找到了一个解决方案XSL recursive sort。
第一部分。排序
很容易使原始解决方案适应新案例。 与接受的答案不同,此解决方案正确排序XML文档,其中有
<val>1.3.2</val>
但没有
<val>1.3</val>
请参阅第II部分,将排序后的结果转换为有用的嵌套列表结构。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="row">
<xsl:sort select="substring-before(concat(val, '.'), '.')"
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 chunks -->
<xsl:when test="val = substring($prefix, 1, string-length($prefix)-1)">
<xsl:copy-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="chunk" select=
"substring-before(concat(substring-after(val, $prefix), '.'), '.')"/>
<!-- this tests for grouping row with same prefix, to skip duplicates -->
<xsl:if test=
"not(preceding-sibling::row[starts-with(val, concat($prefix, $chunk))])">
<xsl:variable name="new-prefix"
select="concat($prefix, $chunk, '.')"/>
<xsl:apply-templates select=
"../row[starts-with(val, $new-prefix) or val = concat($prefix, $chunk)]">
<xsl:sort select=
"substring-before(concat(substring-after(val, $new-prefix), '.'), '.')"
data-type="number"/>
<xsl:with-param name="prefix" select="$new-prefix"/>
</xsl:apply-templates>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
对以下XML文档应用此转换时 - 请注意<val>1.3.2</val>
但没有<val>1.3</val>
,并且接受的答案不会产生正确的结果 - - 实际上删除了<row>
孩子的整个<val>1.3.2</val>
:
<root>
<row>
<col name="rank"/>
<name>A</name>
<val>1.1</val>
</row>
<row>
<col name="rank"/>
<name>B</name>
<val>1</val>
</row>
<row>
<col name="rank"/>
<name>F</name>
<val>1.10</val>
</row>
<row>
<col name="level"/>
<name>C</name>
<val>2</val>
</row>
<row>
<col name="rank"/>
<name>D</name>
<val>1.2.2</val>
</row>
<row>
<col name="rank"/>
<name>D</name>
<val>1.3.2</val>
</row>
<row>
<col name="rank"/>
<name>E</name>
<val>1.2.1</val>
</row>
<row>
<col name="rank"/>
<name>F</name>
<val>1.2</val>
</row>
</root>
生成了正确排序的结果:
<root>
<row>
<col name="rank"/>
<name>B</name>
<val>1</val>
</row>
<row>
<col name="rank"/>
<name>A</name>
<val>1.1</val>
</row>
<row>
<col name="rank"/>
<name>F</name>
<val>1.2</val>
</row>
<row>
<col name="rank"/>
<name>E</name>
<val>1.2.1</val>
</row>
<row>
<col name="rank"/>
<name>D</name>
<val>1.2.2</val>
</row>
<row>
<col name="rank"/>
<name>D</name>
<val>1.3.2</val>
</row>
<row>
<col name="rank"/>
<name>F</name>
<val>1.10</val>
</row>
<row>
<col name="level"/>
<name>C</name>
<val>2</val>
</row>
</root>
最后,还有一个重构:消除所有XSLT条件运算符:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="row">
<xsl:sort select="substring-before(concat(val, '.'), '.')"
data-type="number"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="row">
<xsl:param name="prefix" select="''"/>
<xsl:variable name="vHasChildren" select=
"not(val = substring($prefix, 1, string-length($prefix)-1))"/>
<xsl:copy-of select="self::node()[not($vHasChildren)]"/>
<xsl:variable name="chunk"
select="substring-before(concat(substring-after(val, $prefix), '.'), '.')"/>
<xsl:variable name="new-prefix" select="concat($prefix, $chunk, '.')"/>
<xsl:apply-templates select= "self::node()
[$vHasChildren
and not(preceding-sibling::row[starts-with(val, concat($prefix, $chunk))])
]
/../row[starts-with(val, $new-prefix) or val = concat($prefix, $chunk)]">
<xsl:with-param name="prefix" select="$new-prefix"/>
<xsl:sort data-type="number" select=
"substring-before(concat(substring-after(val, $new-prefix), '.'), '.')"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
第二部分:将已排序的平面结果转换为嵌套列表结构
这里我们从第一部分中产生的转换结果开始,从中我们生成有用的嵌套列表结构。到目前为止我们得到的排序结果是:
<root>
<row>
<col name="rank"/>
<name>B</name>
<val>1</val>
</row>
<row>
<col name="rank"/>
<name>A</name>
<val>1.1</val>
</row>
<row>
<col name="rank"/>
<name>F</name>
<val>1.2</val>
</row>
<row>
<col name="rank"/>
<name>E</name>
<val>1.2.1</val>
</row>
<row>
<col name="rank"/>
<name>D</name>
<val>1.2.2</val>
</row>
<row>
<col name="rank"/>
<name>D</name>
<val>1.3.2</val>
</row>
<row>
<col name="rank"/>
<name>F</name>
<val>1.10</val>
</row>
<row>
<col name="level"/>
<name>C</name>
<val>2</val>
</row>
</root>
我们使用此转换:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<list>
<xsl:apply-templates select=
"row[not(substring-before(concat(val, '.'), '.')
= substring-before(concat(preceding-sibling::row[1]/val,'.'),'.'))]">
<xsl:with-param name="pPrefix" select="''"/>
</xsl:apply-templates>
</list>
</xsl:template>
<xsl:template match="row">
<xsl:param name="pPrefix"/>
<item val="{val}">
<xsl:variable name="vnewPrefix" select="concat($pPrefix, val, '.')"/>
<xsl:variable name="vcurrentVal" select="val"/>
<xsl:apply-templates select="following-sibling::row
[starts-with(val, concat($vcurrentVal,'.'))
and
(string-length(val) - string-length(translate(val,'.',''))
= 1 + string-length($vcurrentVal) - string-length(translate($vcurrentVal,'.','')
)
or
not(starts-with(val,
concat($vnewPrefix,
substring-before(concat(substring-after(preceding-sibling::row[1]/val, $vnewPrefix),'.'),'.'),
'.')
)
)
)
]">
<xsl:with-param name="pPrefix" select="$vnewPrefix"/>
</xsl:apply-templates>
</item>
</xsl:template>
</xsl:stylesheet>
在上述XML文档中应用此转换的结果是有用的嵌套列表结构:
<list>
<item val="1">
<item val="1.1"/>
<item val="1.2">
<item val="1.2.1"/>
<item val="1.2.2"/>
</item>
<item val="1.3.2"/>
<item val="1.10"/>
</item>
<item val="2"/>
</list>
我们可以使用此转换类似地生成所需的HTML:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<ul>
<xsl:apply-templates select=
"row[not(substring-before(concat(val, '.'), '.')
= substring-before(concat(preceding-sibling::row[1]/val,'.'),'.'))]">
<xsl:with-param name="pPrefix" select="''"/>
</xsl:apply-templates>
</ul>
</xsl:template>
<xsl:template match="row">
<xsl:param name="pPrefix"/>
<li> <xsl:value-of select="concat(val, ' - ', name, '
')"/>
<xsl:variable name="vnewPrefix" select="concat($pPrefix, val, '.')"/>
<xsl:variable name="vcurrentVal" select="val"/>
<xsl:variable name="vnextInChain" select=
"following-sibling::row
[starts-with(val, concat($vcurrentVal,'.'))
and
(string-length(val) - string-length(translate(val,'.',''))
= 1 + string-length($vcurrentVal) - string-length(translate($vcurrentVal,'.','')
)
or
not(starts-with(val,
concat($vnewPrefix,
substring-before(concat(substring-after(preceding-sibling::row[1]/val, $vnewPrefix),'.'),'.'),
'.')
)
)
)
]"/>
<xsl:if test="$vnextInChain">
<ul>
<xsl:apply-templates select="following-sibling::row
[starts-with(val, concat($vcurrentVal,'.'))
and
(string-length(val) - string-length(translate(val,'.',''))
= 1 + string-length($vcurrentVal) - string-length(translate($vcurrentVal,'.','')
)
or
not(starts-with(val,
concat($vnewPrefix,
substring-before(concat(substring-after(preceding-sibling::row[1]/val, $vnewPrefix),'.'),'.'),
'.')
)
)
)
]">
<xsl:with-param name="pPrefix" select="$vnewPrefix"/>
</xsl:apply-templates>
</ul>
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>
当对平面排序结果应用此转换时,将生成所需的HTML结果:
<ul>
<li>1 - B
<ul>
<li>1.1 - A
</li>
<li>1.2 - F
<ul>
<li>1.2.1 - E
</li>
<li>1.2.2 - D
</li>
</ul></li>
<li>1.3.2 - D
</li>
<li>1.10 - F
</li>
</ul></li>
<li>2 - C
</li>
</ul>
答案 3 :(得分:0)
检查您选择的XSLT处理器是否支持数字排序(一种排序字符串的方式,其中连续的数字序列被视为数字,因此“第2章”在“第10章”之前排序)。例如,使用XSLT 3.0和XPath 3.1中定义的UCA归类URI,这将是
<xsl:sort select="val"
collation="http://www.w3.org/2013/collation/UCA?numeric=yes"/>
在Saxon中也可以使用数字校对多年的形式
collation="http://saxon.sf.net/collation?alphanumeric=yes"
答案 4 :(得分:0)
如果您能够利用它,这是另一个候选者:XPath 3.1中的新fn:sort函数允许使用复合排序键,以便您可以编写
sort(val, function($x){tokenize($x, '\.')!number()})