使用xsl:sort进行XSLT排序不起作用

时间:2017-05-09 10:18:14

标签: sorting xslt

当我使用xsl:sort。

时,输出xml没有按预期排序

正如您在结果部分中看到的,1.1.1.10,2.10,2.11,2.12未正确排序。

我希望排序列表如预期结果xml所示。

我在这里做错了吗?请帮忙。

输入XML

    <children>
        <child name="1.1.1.1 BDR Enter Customer"  prefix="1.1.1.1"/>
        <child name="1.1.1.10 BDR for Tax Office"  prefix="1.1.1.10"/>
        <child name="1.1.1.2 BDR Enter Customs"  prefix="1.1.1.2"/>
        <child name="1.1.1.3 BDR Enter Employee"  prefix="1.1.1.3"/>
        <child name="1.1.1.4 BDR Enter Forwarder"  prefix="1.1.1.4"/>
        <child name="1.1.1.5 BDR Enter Manufacturer"  prefix="1.1.1.5"/>
        <child name="1.1.1.6 BDR Enter Owner"  prefix="1.1.1.6"/>
        <child name="1.1.1.7 BDR Enter Person"  prefix="1.1.1.7"/>
        <child name="1.1.1.8 BDR Enter Resource"  prefix="1.1.1.8"/>
        <child name="1.1.1.9 BDR Enter Supplier"  prefix="1.1.1.9"/>
        <child name="1.1 Define System Basics" prefix="1.1"/>
        <child name="2.9 Set Up Basic Data Accounting Rules" prefix="2.9"/>
        <child name="2.2 Budget Management"  prefix="2.2"/>
        <child name="2.10 Customer Bill of Exchange Payment"  prefix="2.10"/>
        <child name="2.5 Depreciation Plan"  prefix="2.5"/>
        <child name="2.12 Supplier Credit Invoice"  prefix="2.12"/>
        <child name="2.11 AP/AR Nettings"  prefix="2.11"/>
        <child name="2.3 Arrival Entry Supplier Invoice"  prefix="2.3"/>
        <child name="2.1 Archive Data, Internal Ledger"  prefix="2.1"/>
</children>

我的XSL

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="utf-8" indent="yes"/>
    <xsl:template match="children">
        <children>
            <xsl:for-each select="child">
                <xsl:sort select="@prefix"/>
                <child>
                    <xsl:attribute name="name">
                        <xsl:value-of select="@name"/>
                    </xsl:attribute>
                    <xsl:attribute name="prefix">
                        <xsl:value-of select="@prefix"/>
                    </xsl:attribute>
                </child>
            </xsl:for-each>
        </children>
    </xsl:template>
</xsl:stylesheet>

结果

<?xml version="1.0" encoding="utf-8"?>
<children>
  <child name="1.1 Define System Basics" prefix="1.1"/>
  <child name="1.1.1.1 BDR Enter Customer" prefix="1.1.1.1"/>
  <child name="1.1.1.10 BDR for Tax Office" prefix="1.1.1.10"/>
  <child name="1.1.1.2 BDR Enter Customs" prefix="1.1.1.2"/>
  <child name="1.1.1.3 BDR Enter Employee" prefix="1.1.1.3"/>
  <child name="1.1.1.4 BDR Enter Forwarder" prefix="1.1.1.4"/>
  <child name="1.1.1.5 BDR Enter Manufacturer" prefix="1.1.1.5"/>
  <child name="1.1.1.6 BDR Enter Owner" prefix="1.1.1.6"/>
  <child name="1.1.1.7 BDR Enter Person" prefix="1.1.1.7"/>
  <child name="1.1.1.8 BDR Enter Resource" prefix="1.1.1.8"/>
  <child name="1.1.1.9 BDR Enter Supplier" prefix="1.1.1.9"/>
  <child name="2.1 Archive Data, Internal Ledger" prefix="2.1"/>
  <child name="2.10 Customer Bill of Exchange Payment" prefix="2.10"/>
  <child name="2.11 AP/AR Nettings" prefix="2.11"/>
  <child name="2.12 Supplier Credit Invoice" prefix="2.12"/>
  <child name="2.2 Budget Management" prefix="2.2"/>
  <child name="2.3 Arrival Entry Supplier Invoice" prefix="2.3"/>
  <child name="2.5 Depreciation Plan" prefix="2.5"/>
  <child name="2.9 Set Up Basic Data Accounting Rules" prefix="2.9"/>
</children>

预期结果

<?xml version="1.0" encoding="utf-8"?>
<children>
  <child name="1.1 Define System Basics" prefix="1.1"/>
  <child name="1.1.1.1 BDR Enter Customer" prefix="1.1.1.1"/>
  <child name="1.1.1.2 BDR Enter Customs" prefix="1.1.1.2"/>
  <child name="1.1.1.3 BDR Enter Employee" prefix="1.1.1.3"/>
  <child name="1.1.1.4 BDR Enter Forwarder" prefix="1.1.1.4"/>
  <child name="1.1.1.5 BDR Enter Manufacturer" prefix="1.1.1.5"/>
  <child name="1.1.1.6 BDR Enter Owner" prefix="1.1.1.6"/>
  <child name="1.1.1.7 BDR Enter Person" prefix="1.1.1.7"/>
  <child name="1.1.1.8 BDR Enter Resource" prefix="1.1.1.8"/>
  <child name="1.1.1.9 BDR Enter Supplier" prefix="1.1.1.9"/>
  <child name="1.1.1.10 BDR for Tax Office" prefix="1.1.1.10"/>
  <child name="2.1 Archive Data, Internal Ledger" prefix="2.1"/>
  <child name="2.2 Budget Management" prefix="2.2"/>
  <child name="2.3 Arrival Entry Supplier Invoice" prefix="2.3"/>
  <child name="2.5 Depreciation Plan" prefix="2.5"/>
  <child name="2.9 Set Up Basic Data Accounting Rules" prefix="2.9"/>
  <child name="2.10 Customer Bill of Exchange Payment" prefix="2.10"/>
  <child name="2.11 AP/AR Nettings" prefix="2.11"/>
  <child name="2.12 Supplier Credit Invoice" prefix="2.12"/>
</children>

3 个答案:

答案 0 :(得分:1)

使用XSLT 3.0和XPath 3.1中的sort函数,您可以使用

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    version="3.0">

    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:output indent="yes"/>

    <xsl:template match="children">
        <xsl:copy>
            <xsl:apply-templates select="sort(child, (), function($c) { tokenize($c/@prefix, '\.')!xs:integer(.) })"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Saxon 9.7 PE或EE以及最新版本的Altova XMLSpy或Raptor支持XSLT 3.0。

使用Saxon 9(包括HE),您可以使用collation="http://saxon.sf.net/collation?alphanumeric=yes"上的XSLT 2.0和xsl:sort来执行此操作:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:output indent="yes"/>

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

    <xsl:template match="children">
        <xsl:copy>
            <xsl:apply-templates select="child">
                <xsl:sort select="@prefix" collation="http://saxon.sf.net/collation?alphanumeric=yes"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

  

我在这里做错了吗?

是的,你做错了两件事:

  • 首先,您使用单个排序键进行排序,而实际上您有四个排序键(至少) - @prefix的单独段由'。'分隔字符。

  • 在第二位,您似乎希望每个键按数字排序,但您接受默认的词典排序。

在XSLT中,您可以通过提供多个xsl:sort元素按多个键排序,并且可以通过这些元素的data-type属性进行数字排序。但是,如果您被限制为没有扩展名的XSLT 1.0,那么这仍然会让您在将前缀属性分解为键时遇到一些棘手的问题。对于固定的最大段数,您可以这样做:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" encoding="utf-8" indent="yes"/>

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

  <xsl:template match="children">
    <xsl:copy>
      <xsl:apply-templates select="child">
        <xsl:sort data-type="number"
            select="substring-before(@prefix, '.')"/>
        <xsl:sort data-type="number"
            select="substring-before(substring-after(concat(@prefix, '.0.0.0.'), '.'), '.')"/>
        <xsl:sort data-type="number"
            select="substring-before(substring-after(substring-after(concat(@prefix, '.0.0.0.'), '.'), '.'), '.')"/>
        <xsl:sort data-type="number"
            select="substring-before(substring-after(substring-after(substring-after(concat(@prefix, '.0.0.0.'), '.'), '.'), '.'), '.')"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

请注意使用substring-before()substring-after()函数来挑选“。”前缀。分隔符,以及'.0.0.0.'与前缀的串联,以确保所有前缀都被视为相同,无论深度如何,直到最大支持深度(在这种情况下为四,但可以扩展到任何深度都需要) )。

另请注意,xsl:copy通常优先于文字输出元素xsl:elementxsl:attribute,前者适用,并且排序可应用于{{1 }},也不仅仅是xsl:apply-templates

答案 2 :(得分:0)

如果没有办法通过向前推进到更新的XSLT版本或利用供应商扩展来实现这一点,那么我将解决它的方法是预处理XML修改内容

v <- "    John Andrew Thomas"
paste0(stringi::stri_extract_all_words(v, simplify = TRUE), "@gmail.com", collapse = ";")

#[1] "John@gmail.com;Andrew@gmail.com;Thomas@gmail.com"

然后使用多部分排序键进行排序:

<child name="1.1.1.10 BDR for Tax Office" prefix="1.1.1.10"/>

预处理可以使用一系列指令完成,例如

<child name="1.1.1.10 BDR for Tax Office" prefix="1.1.1.10"
  k1="1" k2="1" k3="1" k4="10"/>