XSLT按字母和数字排序问题

7

我需要将一组字符串进行处理,即 g:lines = '9,1,306,LUCY,G,38,12'

我需要得到的输出结果是 XSLT 1.0 格式的:

1,9,12,38,306,G,LUCY

这是我的当前代码:

<xsl:for-each select="$all_alerts[g:problem!='normal_service'][g:service='bus']">
  <xsl:sort select="g:line"/>
  <xsl:sort select="number(g:line)" data-type="number"/>
  <xsl:value-of select="normalize-space(g:line)" /><xsl:text/>
  <xsl:if test="position()!=last()"><xsl:text>,&#160;</xsl:text></xsl:if>
</xsl:for-each>

我可以让它只显示“1, 12, 306, 38, 9, G, LUCY”,因为第二个排序没有被捕获。有人能帮我吗?

您的列表中有非数字项,它们应该如何进行数值排序? - Oded
我知道这些项目既包括数字又包括字母,我只需要按数字大小排序,然后按字母表顺序排序。我需要做什么才能实现这个目标? - Bryan
4个回答

13
为了仅使用一个xsl:foreach语句实现这个目标,请尝试以下方法:
<xsl:for-each select="$all_alerts[g:problem!='normal_service'][g:service='bus']"> 
  <xsl:sort select="not(number(g:line))"/> 
  <xsl:sort select="number(g:line)" data-type="number"/> 
  <xsl:sort select="g:line"/> 
  <xsl:value-of select="normalize-space(g:line)" /><xsl:text/> 
  <xsl:if test="position()!=last()"><xsl:text>,&#160;</xsl:text></xsl:if> 
</xsl:for-each> 

首先使用xsl:sort 按行是否为数字进行排序。使用 not()函数检查行是否为数字,是数字则返回false,不是数字则返回true。false会排在true前面,从而使数字优先显示。如果省略此排序,则字母会先显示。

接下来的xsl:sort将按数字进行排序,并正确地排序数字,但不影响字母(应用number()时所有字母都返回NaN)。

最后一个xsl:sort将对字母进行字母表排序。


0
<xsl:template match="/">
  <xsl:for-each select="(9,1,306,'LUCY','G',89)" >
    <xsl:sort select="if (number()) then () else ."/>
    <xsl:sort select="number(.)" data-type="number" />
    <xsl:value-of select="."/>
    <xsl:value-of select="', '" />
  </xsl:for-each>
</xsl:template>

给我

1, 9, 89, 306, G, LUCY,

我猜这就是你需要的,对吗?


我收到了一个错误,可能是因为那段代码是针对2.0版本的,而我正在使用1.0版本。javax.xml.transform.TransformerConfigurationException: javax.xml.transform.TransformerException: javax.xml.transform.TransformerException: 找不到函数:if - Bryan
我敢说,你永远不应该习惯XSLT 1.0。它已经远远落后于2.0。很抱歉我无法帮助你,但你的需求相当高级,如果你坚持使用XSLT 1.0,可能会运气不佳。你已经在答案中看到了技巧:只有在项目不是数字时才使用文本值。如果是数字,则返回“nothing”。因此,如果你(或其他人)可以将其映射到XSLT 1.0,那么你就完成了。我会放弃这个问题,因为我再也不想进入XSLT 1.0模式了。再次很抱歉。 - topskip
我明白了,我们公司正在使用一个叫做Alfresco的开源CMS,它不允许我使用XSLT 2.0。感谢您的帮助。 - Bryan

0

在XSLT 1.0中,我认为你需要像这样的东西:

<xsl:for-each select="$all_alerts[g:problem!='normal_service'][g:service='bus']">  
    <xsl:sort select="g:line[number(g:line) != number(g:line)]"/>  
    <xsl:sort select="g:line[number(g:line) = number(g:line)]" data-type="number"/>
    <xsl:value-of select="normalize-space(g:line)" /><xsl:text/>  
    <xsl:if test="position()!=last()"><xsl:text>,&#160;</xsl:text></xsl:if>  
</xsl:for-each>

number($foo) != number($foo) 是用于测试一个值是否为非数字的XSLT 1.0习惯用法。

另一种(更简洁的)解决方案是先选择/排序数字,然后再选择其他内容。


上面的代码给了我这个输出: 1、12、306、38、9、G、LUCY - Bryan

0

我相信这可以实现你想要的功能。

我首先将数字的评估/排序和文本节点值分开处理。

    <xsl:for-each select="$all_alerts[g:problem!='normal_service'][g:service='bus'][number(g:line)=number(g:line)]">
        <xsl:sort select="g:line" data-type="number" order="ascending"/>
        <xsl:value-of select="normalize-space(g:line)" />
        <xsl:text/>
        <xsl:if test="position()!=last() or $all_alerts[g:problem!='normal_service'][g:service='bus'][number(g:line)!=number(g:line)]">
            <xsl:text>,&#160;</xsl:text>
        </xsl:if>
    </xsl:for-each>

    <xsl:for-each select="$all_alerts[g:problem!='normal_service'][g:service='bus'][number(g:line)!=number(g:line)]">
        <xsl:sort select="g:line[number(g:line) != number(g:line)]"/>
        <xsl:value-of select="normalize-space(g:line)" />
        <xsl:text/>
        <xsl:if test="position()!=last()">
            <xsl:text>,&#160;</xsl:text>
        </xsl:if>
    </xsl:for-each>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接