XSLT 2.0 for-each-group按索引计算修改EndDate元素值问题
问题诊断
两个核心错误导致第二个Person的EndDate为空:
- 你在
xsl:for-each-group的分组上下文中直接调用following-sibling::StartDate,获取的是原始Start_Dates节点下未分组去重的后续节点,而非当前分组后排序的节点列表,following-sibling::StartDate[$i+1]在第二个分组中无匹配节点,因此输出空值。 - 你未对分组后的StartDate做显式排序,若原始StartDate顺序紊乱会直接导致EndDate计算逻辑完全失效。
修复思路
- 先将分组去重后的StartDate按日期升序排序,存入变量获得固定顺序的节点列表,直接通过位置索引获取下一个StartDate
- 拆分独立模板实现不同功能:
- 根节点处理模板:负责构造输出XML的根结构
- CompoundEmployee节点处理模板:负责提取去重排序后的日期列表
- Person节点生成模板:负责生成每个Person节点的基础结构
- EndDate计算模板:负责根据当前节点位置计算对应EndDate
- 关联信息拷贝模板:负责拷贝当前日期对应的人员、职位等关联信息,可直接扩展你注释的逻辑
完整修复后的XSLT代码
<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" version="1.0" encoding="UTF-8" indent="yes"/> <!-- 根节点处理模板 --> <xsl:template match="/queryCompoundEmployeeResponse"> <queryCompoundEmployeeResponse> <xsl:apply-templates select="CompoundEmployee"/> </queryCompoundEmployeeResponse> </xsl:template> <!-- CompoundEmployee节点处理模板 --> <xsl:template match="CompoundEmployee"> <xsl:copy> <xsl:variable name="person" select="person" /> <!-- 提取去重排序后的StartDate列表 --> <xsl:variable name="sortedStartDates" as="xs:date*"> <xsl:for-each-group select="Start_Dates/StartDate" group-by="."> <xsl:sort select="xs:date(.)" order="ascending"/> <xsl:sequence select="xs:date(.)"/> </xsl:for-each-group> </xsl:variable> <!-- 遍历生成每个Person节点 --> <xsl:for-each select="$sortedStartDates"> <xsl:call-template name="generatePerson"> <xsl:with-param name="currentStartDate" select="."/> <xsl:with-param name="position" select="position()"/> <xsl:with-param name="totalCount" select="count($sortedStartDates)"/> <xsl:with-param name="sortedStartDates" select="$sortedStartDates"/> <xsl:with-param name="lastEndDate" select="End_Dates/EndDate[last()]"/> <xsl:with-param name="personNode" select="$person"/> </xsl:call-template> </xsl:for-each> </xsl:copy> </xsl:template> <!-- Person节点生成模板 --> <xsl:template name="generatePerson"> <xsl:param name="currentStartDate" as="xs:date"/> <xsl:param name="position" as="xs:integer"/> <xsl:param name="totalCount" as="xs:integer"/> <xsl:param name="sortedStartDates" as="xs:date*"/> <xsl:param name="lastEndDate"/> <xsl:param name="personNode"/> <Person> <StartDate><xsl:value-of select="$currentStartDate"/></StartDate> <!-- 调用EndDate计算模板 --> <xsl:call-template name="calculateEndDate"> <xsl:with-param name="position" select="$position"/> <xsl:with-param name="totalCount" select="$totalCount"/> <xsl:with-param name="sortedStartDates" select="$sortedStartDates"/> <xsl:with-param name="lastEndDate" select="$lastEndDate"/> </xsl:call-template> <!-- 调用关联信息拷贝模板 你注释的代码可以在这里实现 --> <xsl:call-template name="copyRelatedInfo"> <xsl:with-param name="currentStartDate" select="$currentStartDate"/> <xsl:with-param name="personNode" select="$personNode"/> </xsl:call-template> </Person> </xsl:template> <!-- EndDate计算模板 --> <xsl:template name="calculateEndDate"> <xsl:param name="position" as="xs:integer"/> <xsl:param name="totalCount" as="xs:integer"/> <xsl:param name="sortedStartDates" as="xs:date*"/> <xsl:param name="lastEndDate"/> <EndDate> <xsl:choose> <!-- 最后一个节点用输入的最后一个EndDate --> <xsl:when test="$position = $totalCount"> <xsl:value-of select="$lastEndDate"/> </xsl:when> <!-- 其他节点取下一个StartDate的前一天 --> <xsl:otherwise> <xsl:variable name="nextStart" select="$sortedStartDates[$position + 1]"/> <xsl:value-of select="$nextStart - xs:dayTimeDuration('P1D')"/> </xsl:otherwise> </xsl:choose> </EndDate> </xsl:template> <!-- 关联信息拷贝模板 可扩展你需要的拷贝逻辑 --> <xsl:template name="copyRelatedInfo"> <xsl:param name="currentStartDate" as="xs:date"/> <xsl:param name="personNode"/> <!-- 你注释的代码可以在此处实现 示例: <xsl:copy-of select="$personNode/* except $personNode/(personal_information | phone_information | email_information | employment_information)"/> <xsl:copy-of select="$personNode/personal_information[xs:date(start_date) le $currentStartDate and $currentStartDate le xs:date(end_date)]"/> <xsl:copy-of select="$personNode/employment_information[xs:date(start_date) le $currentStartDate and $currentStartDate le xs:date(end_date)]"/> <xsl:copy-of select="$personNode/employment_information/job_information[xs:date(start_date) le $currentStartDate and $currentStartDate le xs:date(end_date)]"/> --> </xsl:template> </xsl:stylesheet>
内容的提问来源于stack exchange,提问作者VoiceOfReason
相关产品推荐
相关产品推荐

