基于值列表对XML在XSLT中进行排序

6

我想使用XSL转换来对一个XML文件进行排序。

<root>
   <element>
        <name>A</name>
   </element>
   <element>
        <name>B</name>
   </element>
   <element>
        <name>C</name>
   </element>
</root>

必须按照以下名称列表排序:C,A,B,以便生成的XML为:

 <root>
       <element>
            <name>C</name>
       </element>
       <element>
            <name>A</name>
       </element>
       <element>
            <name>B</name>
       </element>
    </root>

很明显,需要排序的数值列表应该非常动态(是XSLT的一个参数,还是另一个XML文件...)。您有任何在XSLT中实现的想法吗?
谢谢, Christophe

好问题,+1。请查看我的答案,其中包含完整而简短的解决方案以及详尽的解释。 - Dimitre Novatchev
1个回答

10

This transformation:

这个转换:

<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:param name="pSortingValues" select="'C,A,B'"/>
 <xsl:variable name="vSortingValues" select=
  "concat(',', $pSortingValues, ',')"/>

    <xsl:template match="node()|@*">
      <xsl:copy>
         <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="/*">
      <xsl:copy>
       <xsl:apply-templates select="@*"/>
       <xsl:apply-templates select="*">
        <xsl:sort data-type="number" select=
        "string-length(substring-before($vSortingValues,concat(',',name,',')))"/>
       </xsl:apply-templates>
      </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时:

<root>
    <element>
        <name>A</name>
    </element>
    <element>
        <name>B</name>
    </element>
    <element>
        <name>C</name>
    </element>
</root>

产生所需且正确的结果:

<root>
   <element>
      <name>C</name>
   </element>
   <element>
      <name>A</name>
   </element>
   <element>
      <name>B</name>
   </element>
</root>

请注意:

  1. 所需的已排序值列表是全局参数pSortingValues,可以从外部提供给转换。

  2. 标识规则用于将所有节点“按原样”复制。

  3. 顶层元素覆盖了标识规则。顶层元素被部分复制,其属性被复制,然后模板应用于所有具有<xsl:sort>子指令的子元素,在其中指定要使用的确切排序键 -- 在pSortingValues值前面的element子元素名称。

更新: 如@Alejandro所指出的,这个:

        <xsl:sort data-type="number" select=
        "string-length(substring-before($vSortingValues,concat(',',name,',')))"/>

可以简化为这样:

        <xsl:sort data-type="number" select=
        "substring-before($vSortingValues,concat(',',name,','))"/>

1
@Dimitre:+1 好答案。作为字符串数据类型,前面的子字符串也是以同样的方式工作的。 - user357812
1
@Alejandro:谢谢。我 正在使用 substring-before() - Dimitre Novatchev
1
@Dimitre:是的,我看到了。我的意思是不使用fn:string-length()@data-type="number" - user357812
@Dimitre:抱歉,是的。就像这里中所示:xsl:sort select="substring-before($vSortingValues,concat(',',name,','))"就足够了。 - user357812
@JonH:你想要什么???为什么任何一个理智的人都会用xsl:for-each替换xsl:apply-templates?当然,这是可以做到的,但为什么需要这样的替换呢?而且,这样的替换并不是“解释”,所以我对你的评论感到非常困惑... - Dimitre Novatchev
显示剩余3条评论

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