使用XSLT 2.0按日期降序对XML节点进行排序

问题描述

我有一组节点,这些节点显示来自两个不同系统(不同节点)的工人报酬以及生效日期。无论这些值来自先前系统还是当前系统,我都需要按升/降顺序对其进行排序。

我尝试在for-each中使用sort函数,但是我收到一个错误,即排序时最多可以传递1个节点。

<?xml version="1.0" encoding="UTF-8"?>
<Workers xmlns:ch="test/Compensation_History">
    <ch:Worker_Entry>
        <ch:Employee_ID>12345</ch:Employee_ID>
        <ch:Compensation_History_PrevIoUs_System>
            <ch:Amount>100000</ch:Amount>
            <ch:Effective_Date>2018-01-01-07:00</ch:Effective_Date>
        </ch:Compensation_History_PrevIoUs_System>
        <ch:Compensation_History_PrevIoUs_System>
            <ch:Amount>95000</ch:Amount>
            <ch:Effective_Date>2017-01-01-07:00</ch:Effective_Date>
        </ch:Compensation_History_PrevIoUs_System>
        <ch:Compensation_History_PrevIoUs_System>
            <ch:Amount>90000</ch:Amount>
            <ch:Effective_Date>2016-01-01-07:00</ch:Effective_Date>
        </ch:Compensation_History_PrevIoUs_System>
        <ch:Compensation_History_Current_System>
            <ch:Amount>105000</ch:Amount>
            <ch:Effective_Date>2019-01-01-07:00</ch:Effective_Date>
        </ch:Compensation_History_Current_System>
        <ch:Compensation_History_Current_System>
            <ch:Amount>110000</ch:Amount>
            <ch:Effective_Date>2020-01-01-07:00</ch:Effective_Date>
        </ch:Compensation_History_Current_System>
    </ch:Worker_Entry>
</Workers>

预期输出

<?xml version="1.0" encoding="UTF-8"?>
<Workers xmlns:ch="test/Compensation_History">
    <ch:Worker_Entry>
        <ch:Employee_ID>12345</ch:Employee_ID>
        <ch:Compensation_History_Current_System>
            <ch:Amount>110000</ch:Amount>
            <ch:Effective_Date>2020-01-01-07:00</ch:Effective_Date>
        </ch:Compensation_History_Current_System>
        <ch:Compensation_History_Current_System>
            <ch:Amount>105000</ch:Amount>
            <ch:Effective_Date>2019-01-01-07:00</ch:Effective_Date>
        </ch:Compensation_History_Current_System>
        <ch:Compensation_History_PrevIoUs_System>
            <ch:Amount>100000</ch:Amount>
            <ch:Effective_Date>2018-01-01-07:00</ch:Effective_Date>
        </ch:Compensation_History_PrevIoUs_System>
        <ch:Compensation_History_PrevIoUs_System>
            <ch:Amount>95000</ch:Amount>
            <ch:Effective_Date>2017-01-01-07:00</ch:Effective_Date>
        </ch:Compensation_History_PrevIoUs_System>
        <ch:Compensation_History_PrevIoUs_System>
            <ch:Amount>90000</ch:Amount>
            <ch:Effective_Date>2016-01-01-07:00</ch:Effective_Date>
        </ch:Compensation_History_PrevIoUs_System>
    </ch:Worker_Entry>
</Workers>

下面是我尝试过的代码

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:ch="test/Compensation_History" exclude-result-prefixes="xs"
    version="2.0">

    <xsl:template match="/">
        <Workers>
            <xsl:for-each-group select="ch:Workers" group-by="ch:Employee_ID">
                <xsl:sort select="//ch:Effective_Date" order="descending" />
                <xsl:copy-of select="." />
            </xsl:for-each-group>
        </Workers>
    </xsl:template>
</xsl:stylesheet>

解决方法

在XSLT 3中,您可以使用currentDocument函数

sort

<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xpath-default-namespace="test/Compensation_History" exclude-result-prefixes="#all" expand-text="yes"> <xsl:mode on-no-match="shallow-copy"/> <xsl:output indent="yes"/> <xsl:template match="Worker_Entry"> <xsl:copy> <xsl:apply-templates select="@*,Employee_ID,(* except Employee_ID) => sort((),function($c) { $c/Effective_Date => xs:date() } ) => reverse()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> 作为高级功能需要Saxon 10所有版本或Saxon 9.8及更高版本的PE或EE或Saxon-JS 2。

在您添加的代码中,尽管fn:sort没有任何意义,因为根元素不在<xsl:for-each-group select="ch:Workers" group-by="ch:Employee_ID">命名空间中,并且仅对单个元素进行分组,因此您试图对它们进行分组和排序是否提供分组依据表达式会导致该元素具有多个分组键;在这种情况下,您的表达式ch毫无意义。至于尝试使用ch:Employee_ID,请在此处使用相对表达式,例如如果您想对组进行分组和排序,xsl:sort可能很有意义。

,

如果您确实需要XSLT 2解决方案,那么以下方法应该有效:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:ch="test/Compensation_History" exclude-result-prefixes="xs"
  version="2.0">
  
  <xsl:template match="Workers">
    <xsl:copy>
      <xsl:for-each select="ch:Worker_Entry">
        <xsl:copy>
          <xsl:copy-of select="ch:Employee_ID"/>
          <xsl:for-each select="ch:Compensation_History_Previous_System | ch:Compensation_History_Current_System">
            <xsl:sort select="xs:date(ch:Effective_Date)" order="descending"/>
            <xsl:copy-of select="."/>
          </xsl:for-each>
        </xsl:copy>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>