如何使用XSLT按特定日期范围对XML元素分组?
问题描述
现有如下XML数据,需通过XSLT按特定日期范围重新分组元素。每个<cust_MDFCostDistribution_child>元素包含cust_startdate和cust_EndDate日期范围,仅保留对应日期范围内的元素;当元素到期后,剩余元素从次日起重新分组,effectiveDate为cust_EndDate + xs:dayTimeDuration('P1D')。尝试过group-starting-with和group-ending-with函数但未解决问题,寻求解决方案。
输入XML
<cust_CD> <user> <userId>50298867</userId> <cust_MDFCostDistribution_child> <cust_EndDate>2023-05-31T00:00:00.000</cust_EndDate> <cust_startdate>2023-01-01T00:00:00.000</cust_startdate> <cust_percentage>20</cust_percentage> <cust_costCenter>N3020050</cust_costCenter> <cust_MDFCostDistribution_Parent_usersSysId>50298867</cust_MDFCostDistribution_Parent_usersSysId> </cust_MDFCostDistribution_child> <cust_MDFCostDistribution_child> <cust_EndDate>2023-02-28T00:00:00.000</cust_EndDate> <cust_startdate>2023-01-01T00:00:00.000</cust_startdate> <cust_percentage>40</cust_percentage> <cust_costCenter>NT7000003</cust_costCenter> <cust_MDFCostDistribution_Parent_usersSysId>50298867</cust_MDFCostDistribution_Parent_usersSysId> </cust_MDFCostDistribution_child> <cust_MDFCostDistribution_child> <cust_EndDate>2023-01-31T00:00:00.000</cust_EndDate> <cust_startdate>2023-01-01T00:00:00.000</cust_startdate> <cust_percentage>40</cust_percentage> <cust_costCenter>N2030020</cust_costCenter> <cust_MDFCostDistribution_Parent_usersSysId>50298867</cust_MDFCostDistribution_Parent_usersSysId> </cust_MDFCostDistribution_child> </user> </cust_CD>
期望输出XML
<group> <date> <effectiveDate>2023-01-01T00:00:00.000</effectiveDate> <cust_MDFCostDistribution_child> <cust_EndDate>2023-05-31T00:00:00.000</cust_EndDate> <cust_startdate>2023-01-01T00:00:00.000</cust_startdate> <cust_percentage>20</cust_percentage> <cust_costCenter>N3020050</cust_costCenter> <cust_MDFCostDistribution_Parent_usersSysId>50298867</cust_MDFCostDistribution_Parent_usersSysId> </cust_MDFCostDistribution_child> <cust_MDFCostDistribution_child> <cust_EndDate>2023-02-28T00:00:00.000</cust_EndDate> <cust_startdate>2023-01-01T00:00:00.000</cust_startdate> <cust_percentage>40</cust_percentage> <cust_costCenter>NT7000003</cust_costCenter> <cust_MDFCostDistribution_Parent_usersSysId>50298867</cust_MDFCostDistribution_Parent_usersSysId> </cust_MDFCostDistribution_child> <cust_MDFCostDistribution_child> <cust_EndDate>2023-01-31T00:00:00.000</cust_EndDate> <cust_startdate>2023-01-01T00:00:00.000</cust_startdate> <cust_percentage>40</cust_percentage> <cust_costCenter>N2030020</cust_costCenter> <cust_MDFCostDistribution_Parent_usersSysId>50298867</cust_MDFCostDistribution_Parent_usersSysId> </cust_MDFCostDistribution_child> </date> <date> <effectiveDate>2023-02-01T00:00:00.000</effectiveDate> <cust_MDFCostDistribution_child> <cust_EndDate>2023-05-31T00:00:00.000</cust_EndDate> <cust_startdate>2023-01-01T00:00:00.000</cust_startdate> <cust_percentage>20</cust_percentage> <cust_costCenter>N3020050</cust_costCenter> <cust_MDFCostDistribution_Parent_usersSysId>50298867</cust_MDFCostDistribution_Parent_usersSysId> </cust_MDFCostDistribution_child> <cust_MDFCostDistribution_child> <cust_EndDate>2023-02-28T00:00:00.000</cust_EndDate> <cust_startdate>2023-01-01T00:00:00.000</cust_startdate> <cust_percentage>40</cust_percentage> <cust_costCenter>NT7000003</cust_costCenter> <cust_MDFCostDistribution_Parent_usersSysId>50298867</cust_MDFCostDistribution_Parent_usersSysId> </cust_MDFCostDistribution_child> </date> <date> <effectiveDate>2023-03-01T00:00:00.000</effectiveDate> <cust_MDFCostDistribution_child> <cust_EndDate>2023-05-31T00:00:00.000</cust_EndDate> <cust_startdate>2023-01-01T00:00:00.000</cust_startdate> <cust_percentage>20</cust_percentage> <cust_costCenter>N3020050</cust_costCenter> <cust_MDFCostDistribution_Parent_usersSysId>50298867</cust_MDFCostDistribution_Parent_usersSysId> </cust_MDFCostDistribution_child> </date> <date> <effectiveDate>2023-06-01T00:00:00.000</effectiveDate> </date> </group>
逻辑说明
- 仅保留落在对应日期范围内的元素
- 元素到期后,剩余元素从次日起重新分组,
effectiveDate为cust_EndDate + xs:dayTimeDuration('P1D') - 示例分组:
- 2023-01-01至01-31:三个元素有效
- 2023-02-01至02-28:两个元素有效
- 2023-03-01至05-31:一个元素有效
- 2023-06-01起:无有效元素
XSLT解决方案
使用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"> <xsl:output method="xml" indent="yes"/> <!-- 获取所有关键日期:起始日期、所有到期日期+1天 --> <xsl:variable name="allDates"> <xsl:variable name="startDate" select="xs:dateTime(/cust_CD/user/cust_MDFCostDistribution_child[1]/cust_startdate)"/> <date><xsl:value-of select="$startDate"/></date> <xsl:for-each select="/cust_CD/user/cust_MDFCostDistribution_child"> <date><xsl:value-of select="xs:dateTime(cust_EndDate) + xs:dayTimeDuration('P1D')"/></date> </xsl:for-each> </xsl:variable> <!-- 去重并按日期排序 --> <xsl:variable name="sortedUniqueDates" select="distinct-values($allDates/date/xs:dateTime(.))" as="xs:dateTime*"> <xsl:sort select="."/> </xsl:variable> <xsl:template match="/"> <group> <xsl:for-each select="$sortedUniqueDates"> <xsl:variable name="currentEffectiveDate" select="."/> <date> <effectiveDate><xsl:value-of select="$currentEffectiveDate"/></effectiveDate> <!-- 筛选当前生效日期范围内的元素 --> <xsl:for-each select="/cust_CD/user/cust_MDFCostDistribution_child[ xs:dateTime(cust_startdate) le $currentEffectiveDate and $currentEffectiveDate le xs:dateTime(cust_EndDate) ]"> <xsl:copy-of select="."/> </xsl:for-each> </date> </xsl:for-each> </group> </xsl:template> </xsl:stylesheet>
方案解释
- 提取关键日期:收集所有元素的起始日期、每个元素到期日期的次日,这些日期是分组的节点
- 去重排序:对收集到的日期去重并按时间顺序排序,确保分组按时间推进
- 生成分组:遍历每个关键日期,将其设为
effectiveDate,筛选出所有在该日期仍有效的元素(起始日期≤当前生效日期≤结束日期),并复制到对应分组中 - 空分组处理:当某一关键日期没有有效元素时,自动生成仅包含
effectiveDate的空<date>节点,符合输出要求
内容的提问来源于stack exchange,提问作者SDutta
相关产品推荐
相关产品推荐

