如何在XSLT 2.0中对XML的连续Time_Off_Date分组生成CSV?
当然可以!用XSLT 2.0轻松实现无递归的连续日期分组转CSV
刚接触XSLT的话,你可能还没意识到——XSLT 2.0的xsl:for-each-group配合group-adjacent属性,简直是处理这类连续序列分组问题的神器,完全不需要写递归函数。我给你一步步拆解怎么实现:
先假设你的XML结构(如果和实际有出入,调整XPath就行)
比如常见的请假记录XML大概是这样:
<TimeOffRecords> <Record> <EmployeeID>EMP001</EmployeeID> <Time_Off_Date>2024-05-01</Time_Off_Date> <Reason>Vacation</Reason> </Record> <Record> <EmployeeID>EMP001</EmployeeID> <Time_Off_Date>2024-05-02</Time_Off_Date> <Reason>Vacation</Reason> </Record> <Record> <EmployeeID>EMP001</EmployeeID> <Time_Off_Date>2024-05-04</Time_Off_Date> <Reason>Sick Leave</Reason> </Record> <Record> <EmployeeID>EMP002</EmployeeID> <Time_Off_Date>2024-05-01</Time_Off_Date> <Reason>Vacation</Reason> </Record> </TimeOffRecords>
对应的XSLT 2.0代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs"> <!-- 输出纯文本格式的CSV,禁用缩进,确保换行正确 --> <xsl:output method="text" encoding="UTF-8" indent="no"/> <!-- 先输出CSV表头 --> <xsl:template match="/"> <xsl:text>EmployeeID,StartDate,EndDate,Reason </xsl:text> <xsl:apply-templates select="TimeOffRecords/Record"/> </xsl:template> <!-- 核心分组逻辑:先按员工ID分组,再在每组内按连续日期分组 --> <xsl:template match="Record"> <!-- 第一步:把同一个员工的记录归为一组 --> <xsl:for-each-group select="." group-by="EmployeeID"> <!-- 第二步:在员工组内,把连续的日期归为一组 --> <xsl:for-each-group select="current-group()" group-adjacent="xs:date(Time_Off_Date) - xs:dayTimeDuration('P' || position() - 1 || 'D')"> <!-- 输出当前连续日期组的CSV行 --> <xsl:value-of select="current-group()[1]/EmployeeID"/> <xsl:text>,</xsl:text> <xsl:value-of select="current-group()[1]/Time_Off_Date"/> <!-- 连续日期的起始 --> <xsl:text>,</xsl:text> <xsl:value-of select="current-group()[last()]/Time_Off_Date"/> <!-- 连续日期的结束 --> <xsl:text>,</xsl:text> <xsl:value-of select="current-group()[1]/Reason"/> <!-- 假设同一连续组的原因一致,取第一个 --> <xsl:text> </xsl:text> <!-- 换行 --> </xsl:for-each-group> </xsl:for-each-group> </xsl:template> </xsl:stylesheet>
关键逻辑解释
- 双重分组:先按
EmployeeID分组,避免不同员工的日期被错误合并;再在每个员工的记录里处理连续日期。 - 连续日期分组的核心:
group-adjacent="xs:date(Time_Off_Date) - xs:dayTimeDuration('P' || position() - 1 || 'D')"
这个表达式的作用是:把每个日期转换成xs:date类型,然后减去它在当前员工组内的位置对应的天数(比如第1条记录减0天,第2条减1天,以此类推)。连续的日期会得到同一个基准日期,比如2024-05-01减0天是2024-05-01,2024-05-02减1天也是2024-05-01,所以这两个会被分到同一组;而2024-05-04减2天是2024-05-02,和前面的基准不同,就会单独成组。 - CSV输出:每组连续日期输出一行,包含员工ID、起始日期、结束日期、请假原因,用逗号分隔,每行末尾加换行符。
调整提示
如果你的XML字段名、结构和示例不一样,只需要修改对应的XPath即可:
- 比如如果
Time_Off_Date在别的节点下,就把Time_Off_Date改成完整路径,比如LeaveDetails/Time_Off_Date - 如果需要输出更多字段,在CSV行里添加对应的
<xsl:value-of>和分隔符就行
内容的提问来源于stack exchange,提问作者Ankita
相关产品推荐
相关产品推荐

