如何在 XSLT 中对连续日期进行分组?



我有一个 xml 文件(下面的示例),我想根据连续Time_Off_Date对这个 xml 进行分组。

<Root>
<Entry>
<Employee_ID>101</Employee_ID>
<Time_Off_Details>
<Time_Off_Date>2017-12-01</Time_Off_Date>
</Time_Off_Details>
<Time_Off_Details>
<Time_Off_Date>2017-12-02</Time_Off_Date>
</Time_Off_Details>
<Time_Off_Details>
<Time_Off_Date>2017-12-04</Time_Off_Date>
</Time_Off_Details>
<Time_Off_Details>
<Time_Off_Date>2017-12-05</Time_Off_Date>
</Time_Off_Details> 
</Entry>
<Entry>
<Employee_ID>102</Employee_ID>
<Time_Off_Details>
<Time_Off_Date>2017-12-10</Time_Off_Date>
</Time_Off_Details>
<Time_Off_Details>
<Time_Off_Date>2017-12-13</Time_Off_Date>
</Time_Off_Details>
<Time_Off_Details>
<Time_Off_Date>2017-12-14</Time_Off_Date>
</Time_Off_Details>
</Entry>
</Root>

最终输出应如下所示(CSV 格式)。

Employee ID   Time Off Start  Time Off End
101           12/1/2017       12/2/2017
101           12/4/2017       12/5/2017
102           12/10/2017      12/10/2017
102           12/13/2017      12/14/2017

有没有办法使用 XSLT 2.0 而不使用递归函数来实现这一点? 我是XSLT的新手,所以任何建议都值得赞赏。

如果逻辑是输入 XML 只保留个别休息日,并且您希望将这些恰好连续的单独日子分组,则可以使用xsl:for-each-group选择group-starting-with设置为Time_Off_Date与前一个元素不连续的元素的Time_Off_Details

试试这个 XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="2.0">
<xsl:output method="text" />
<xsl:strip-space elements="*" />
<xsl:template match="Entry">
<xsl:for-each-group select="Time_Off_Details" 
group-starting-with="*[not(xs:date(Time_Off_Date) = xs:date(preceding-sibling::*[1]/Time_Off_Date) + xs:dayTimeDuration('P1D'))]">
<xsl:value-of select="../Employee_ID" />
<xsl:text>,</xsl:text>
<xsl:value-of select="Time_Off_Date" />
<xsl:text>,</xsl:text>
<xsl:value-of select="current-group()[last()]/Time_Off_Date" />
<xsl:text>&#10;</xsl:text>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>

这可以在 XQuery 3 中使用翻转窗口子句 (https://www.w3.org/TR/xquery-31/#id-tumbling-windows) 很好地表达:

for $entry in Root/Entry
for tumbling window $date in $entry//Time_Off_Date/xs:date(.)
start $s when true()
end $e next $n when $n - $e gt xs:dayTimeDuration('P1D')
return string-join(($entry/Employee_ID, $date[1], $date[last()]), '&#9;')

http://xqueryfiddle.liberty-development.net/6qM2e25

由于 XSLT 2 处理器(如 Saxon 9 或 XmlPrime)也支持 XQuery,因此这可能是使用 XSLT 的替代方案。

对于 XSLT,您可能需要解释为什么不想使用递归函数。

可以使用for-each-groupXSLT 2.0中完成您的任务。

首先,您必须按其完整内容对所有Time_Off_Date元素进行排序。

每个组都以一个Time_Off_Date元素开头,而它没有Time_Off_Date存在内容等于 上一个日期,与当前日期相比。

若要以字符串形式计算上一个日期,需要以下序列:

  • 以当前日期为例。
  • 减去 1 天的周期。
  • 将其格式化为yyyy-mm-dd

然后,对于每个组,您需要:

  • 读取第一个小组成员的日期。
  • 读取最后一个小组成员的日期。
  • 打印Employee_ID和两个日期,并根据需要设置格式。

所以整个脚本可以如下所示:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="Root">
<xsl:text>Employee ID,Time Off Start,Time Off End&#xA;</xsl:text>
<xsl:for-each-group select="Entry/Time_Off_Details/Time_Off_Date"
group-starting-with=".[not(//Entry/Time_Off_Details/Time_Off_Date[. =
format-date(xs:date(current()) - xs:dayTimeDuration('P1D'),
'[Y0001]-[M01]-[D01]')])]">
<xsl:sort select="."/>
<xsl:variable name="startDate" select="current-group()[1]"/>
<xsl:variable name="lastDate" select="current-group()[last()]"/>
<xsl:value-of select="../../Employee_ID"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="format-date($startDate,'[M01]/[D1]/[Y0001]')"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="format-date($lastDate,'[M01]/[D1]/[Y0001]')"/>
<xsl:text>&#xA;</xsl:text>
</xsl:for-each-group>
</xsl:template>
</xsl:transform>

相关内容

  • 没有找到相关文章

最新更新