我有一个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的新手,所以任何建议都表示赞赏。
答案 0 :(得分:1)
这可以使用翻滚窗口子句(https://www.w3.org/TR/xquery-31/#id-tumbling-windows)在XQuery 3中很好地表达:
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()]), '	')
http://xqueryfiddle.liberty-development.net/6qM2e25
由于像Saxon 9或XmlPrime这样的XSLT 2处理器也支持XQuery,因此这可能是使用XSLT的替代方案。
对于XSLT,您可能需要解释为什么不想使用递归函数。
答案 1 :(得分:1)
如果逻辑是输入XML仅关闭个别假日,并且您希望将这些日期分组连续,那么您可以使用xsl:for-each-group
选择Time_Off_Details
group-starting-with
设置为Time_Off_Date
与前一个元素不连续的元素。
试试这个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> </xsl:text>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
答案 2 :(得分:0)
您的任务可以使用for-each-group
在 XSLT 2.0 中完成。
首先,您必须按其完整内容对所有Time_Off_Date
元素进行排序。
每个组都以Time_Off_Date
元素开头,而不是Time_Off_Date
元素
存在任何其他yyyy-mm-dd
元素,其内容等于
上一个日期,与当前日期相比。
要计算上一个日期,作为字符串,需要以下序列:
<?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
</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>
</xsl:text>
</xsl:for-each-group>
</xsl:template>
</xsl:transform>
。然后,对于您需要的每个小组:
所以整个脚本可能如下所示:
--expose