如何用XSLT匹配XML节点日期,返回对应cust_Overtime_HR_ACTION元素
问题:XSLT匹配节点并追加对应元素
免责声明:我仍在学习XML/XSLT,若问题较为基础,敬请谅解。
我需要实现以下需求:在XML的A节点中查找元素,匹配B节点中的相似元素,并将B节点的对应条目返回至A节点。具体来说,找到<EmployeeTimeSheetEntry>节点下与<EmployeeTimeValuationResult>/bookingDate匹配的startDate,返回对应的<cust_Overtime_HR_ACTION>元素。
我参考相关方案尝试使用key创建查找表,但不清楚<apply-templates>语句的正确放置位置,目前编写的XSLT代码导致样式表编译报错(工具提示:无法使用提供的XML/XSL输入生成XML文档,样式表编译期间报告错误),无法理清节点检索的逻辑。
示例源XML
<EmployeeTimeSheet> <EmployeeTimeSheet> <employeeTimeSheetEntry> <EmployeeTimeSheetEntry> <cust_Overtime_HR_ACTION>TOIL</cust_Overtime_HR_ACTION> <startDate>2024-11-25T00:00:00.000</startDate> </EmployeeTimeSheetEntry> <EmployeeTimeSheetEntry> <cust_Overtime_HR_ACTION>TOIL</cust_Overtime_HR_ACTION> <startDate>2024-11-27T00:00:00.000</startDate> </EmployeeTimeSheetEntry> <EmployeeTimeSheetEntry> <cust_Overtime_HR_ACTION>Payout</cust_Overtime_HR_ACTION> <startDate>2024-11-30T00:00:00.000</startDate> </EmployeeTimeSheetEntry> </employeeTimeSheetEntry> <employeeTimeValuationResult> <EmployeeTimeValuationResult> <bookingDate>2024-11-25T00:00:00.000</bookingDate> </EmployeeTimeValuationResult> </employeeTimeValuationResult> </EmployeeTimeSheet> </EmployeeTimeSheet>
尝试的XSLT代码(编译报错)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <!-- generate lookup table - must be top level, i.e. before template match --> <xsl:key name="timeSheetEntry" match="EmployeeTimeSheet/EmployeeTimeSheet/employeeTimeSheetEntry/EmployeeTimeSheetEntry" use="startDate"/> <xsl:template match="/"> <EmployeeTimeSheet> <!-- retrieve cust_Overtime_HR_ACTION where the bookingDate matches the startDate in above lookup --> <xsl:template match="/"> <xsl:apply-templates select="key('timeSheetEntry', EmployeeTimeSheet/EmployeeTimeSheet/employeeTimeValuationResult/EmployeeTimeValuationResult/bookingDate)/cust_Overtime_HR_ACTION"/> </xsl:template> <xsl:for-each select="EmployeeTimeSheet/EmployeeTimeSheet"> <xsl:for-each select="employeeTimeValuationResult/EmployeeTimeValuationResult"> <xsl:variable name="var_TimeValResult" select="./*"></xsl:variable> <EmployeeTimeSheet> <xsl:copy-of select="$var_TimeValResult"/> <xsl:template match="cust_Overtime_HR_ACTION"> <overtimeHRaction> <xsl:value-of select="."/> </overtimeHRaction> </xsl:template> </EmployeeTimeSheet> </xsl:for-each> </xsl:for-each> <!-- EmployeeTimeSheet/EmployeeTimeSheet --> </EmployeeTimeSheet> </xsl:template> </xsl:stylesheet>
期望输出
<EmployeeTimeSheet> <EmployeeTimeSheet> <employeeTimeValuationResult> <EmployeeTimeValuationResult> <bookingDate>2024-11-25T00:00:00.000</bookingDate> <cust_Overtime_HR_ACTION>TOIL</cust_Overtime_HR_ACTION> </EmployeeTimeValuationResult> </employeeTimeValuationResult> </EmployeeTimeSheet> </EmployeeTimeSheet>
解决方案
错误分析
- 模板嵌套违规:XSLT要求所有
<xsl:template>必须是<xsl:stylesheet>的直接子节点,不能嵌套在其他模板或for-each内部,这是编译报错的核心原因。 - key路径冗余:key的匹配模式无需写完整路径,只要能定位到目标节点即可,简化后更通用。
- 逻辑混乱:同时混用
apply-templates和for-each,且未正确关联bookingDate与startDate的匹配逻辑,也未保留原XML的结构层级。
正确的XSLT代码
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <!-- 定义key:以startDate为键,匹配所有EmployeeTimeSheetEntry节点 --> <xsl:key name="timeSheetByDate" match="EmployeeTimeSheetEntry" use="startDate"/> <!-- 身份模板:复制所有未被自定义模板匹配的节点和属性 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 移除不需要保留的employeeTimeSheetEntry节点 --> <xsl:template match="employeeTimeSheetEntry"/> <!-- 为EmployeeTimeValuationResult节点追加匹配的cust_Overtime_HR_ACTION --> <xsl:template match="EmployeeTimeValuationResult"> <xsl:copy> <!-- 复制原节点的所有内容 --> <xsl:apply-templates select="@*|node()"/> <!-- 通过key查找对应startDate等于当前bookingDate的EmployeeTimeSheetEntry,复制其cust_Overtime_HR_ACTION --> <xsl:copy-of select="key('timeSheetByDate', bookingDate)/cust_Overtime_HR_ACTION"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
代码说明
- key定义:用
startDate作为索引键,匹配所有EmployeeTimeSheetEntry节点,后续可以快速通过日期查找对应节点。 - 身份模板:默认复制所有节点和属性,确保原XML的大部分结构被保留,不需要手动复制每个节点。
- 移除冗余节点:通过空模板匹配
employeeTimeSheetEntry,使其不被复制到输出中,符合期望输出的结构。 - 追加目标元素:针对
EmployeeTimeValuationResult节点,先复制原有内容,再通过key('timeSheetByDate', bookingDate)找到日期匹配的EmployeeTimeSheetEntry,复制其cust_Overtime_HR_ACTION元素追加到当前节点内。
运行该XSLT即可得到你期望的输出结果。
内容的提问来源于stack exchange,提问作者AJW
相关产品推荐
相关产品推荐

