XSLT3.0累加器Map仅返回最后元素问题排查求助
问题
使用XSLT 3.0的accumulator map时,无论匹配情况如何,查询总是仅返回最后一个元素。预期行为是:针对cal:timeoff节点,将其中的cal:person元素值与wd:Report_Data节点下wd:Report_Entry中的wd:Person_Id和wd:Wiser_ID映射关联,返回对应的Wiser_ID。
输入XML
<?xml version="1.0" encoding="utf-8"?> <AggregatedData xmlns:wkd="urn:Workday_timeoff_Extract" xmlns:cal="urn:calabrio" xmlns:wd="urn:com.workday.report/CR_Calabrio_TO_Eligible_Worker_Report"> <wd:Report_Data> <wd:Report_Entry> <wd:Person_Id>473937f5922241879d30afe000c44702</wd:Person_Id> <wd:Wiser_ID>10636</wd:Wiser_ID> </wd:Report_Entry> <wd:Report_Entry> <wd:Person_Id/> <wd:Wiser_ID>EE1111</wd:Wiser_ID> </wd:Report_Entry> </wd:Report_Data> <cal:stack> <cal:timeoff> <cal:person>473937f5922241879d30afe000c44702</cal:person> <cal:key>20241101USAABS002450</cal:key> <cal:date>2024-11-01</cal:date> <cal:timeofftype>USA_ABS002</cal:timeofftype> <cal:hours>4.50</cal:hours> </cal:timeoff> </cal:stack> <wkd:TimeOff-Stack> <wkd:TimeOff-Entry> <wkd:WiserID>10636</wkd:WiserID> <wkd:wd-key>20241107USAABS002800</wkd:wd-key> <wkd:Date>2024-11-07</wkd:Date> <wkd:Timeoff>USA_ABS002</wkd:Timeoff> <wkd:Hours>8.00</wkd:Hours> <wkd:EntryID>TIME_OFF_ENTRY-6-1136743</wkd:EntryID> </wkd:TimeOff-Entry> </wkd:TimeOff-Stack> </AggregatedData>
现有XSLT代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wkd="urn:Workday_timeoff_Extract" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:cal="urn:calabrio" xmlns:wd="urn:com.workday.report/CR_Calabrio_TO_Eligible_Worker_Report" exclude-result-prefixes="#all"> <xsl:output method="xml" indent = "yes" /> <xsl:mode streamable="no" on-no-match="shallow-copy" use-accumulators="calpersonids idmap"/> <xsl:accumulator name="calpersonids" as="xs:string" initial-value="''" streamable="no"> <xsl:accumulator-rule match="wd:Person_Id/text()" select="normalize-space()"/> </xsl:accumulator> <xsl:accumulator name="idmap" as="map(xs:string,xs:string)" initial-value="map{}" streamable="no"> <xsl:accumulator-rule match="wd:Wiser_ID/text()" select="map:put($value, accumulator-before('calpersonids'),normalize-space())"/> </xsl:accumulator> <xsl:template match="AggregatedData"> <xsl:apply-templates select="copy-of(cal:stack/cal:timeoff)"/> </xsl:template> <xsl:template match="cal:timeoff"> <cal:timeoff> <cal:wiserid><xsl:value-of select="accumulator-before('idmap')(cal:person)"/></cal:wiserid> <cal:personid><xsl:value-of select="cal:person"/></cal:personid> <cal:key><xsl:value-of select="cal:key"/></cal:key> <cal:date><xsl:value-of select="cal:date"/></cal:date> <cal:timeofftype><xsl:value-of select="cal:timeofftype"/></cal:timeofftype> <cal:hours><xsl:value-of select="cal:hours"/></cal:hours> </cal:timeoff> </xsl:template> </xsl:stylesheet>
当前输出
<?xml version="1.0" encoding="UTF-8"?> <cal:timeoff xmlns:cal="urn:calabrio"> <cal:wiserid>EE1111</cal:wiserid> <cal:personid>473937f5922241879d30afe000c44702</cal:personid> <cal:key>20241101USAABS002450</cal:key> <cal:date>2024-11-01</cal:date> <cal:timeofftype>USA_ABS002</cal:timeofftype> <cal:hours>4.50</cal:hours> </cal:timeoff>
期望输出
<?xml version="1.0" encoding="UTF-8"?> <cal:timeoff xmlns:cal="urn:calabrio"> <cal:wiserid>10636</cal:wiserid> <cal:key>20241101USAABS002450</cal:key> <cal:date>2024-11-01</cal:date> <cal:timeofftype>USA_ABS002</cal:timeofftype> <cal:hours>4.50</cal:hours> </cal:timeoff>
解决方案
问题原因
calpersonids累加器未重置:当前calpersonids仅在匹配到wd:Person_Id/text()时更新,但第二个wd:Report_Entry中的wd:Person_Id是空节点,无文本节点,导致累加器保留第一个Person_Id的值。处理第二个Wiser_ID时,会用该旧值覆盖map中对应条目,最终473937f5922241879d30afe000c44702对应的Wiser_ID被替换为EE1111。- 空Person_Id处理缺失:未针对空
wd:Person_Id节点重置calpersonids,导致无效映射被写入map。
修改后的XSLT代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wkd="urn:Workday_timeoff_Extract" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:cal="urn:calabrio" xmlns:wd="urn:com.workday.report/CR_Calabrio_TO_Eligible_Worker_Report" exclude-result-prefixes="#all"> <xsl:output method="xml" indent = "yes" /> <xsl:mode streamable="no" on-no-match="shallow-copy" use-accumulators="calpersonids idmap"/> <xsl:accumulator name="calpersonids" as="xs:string?" initial-value="()" streamable="no"> <!-- 匹配wd:Person_Id节点,无论是否有文本都更新值 --> <xsl:accumulator-rule match="wd:Person_Id" select="normalize-space(.)"/> <!-- 每个wd:Report_Entry结束时重置累加器,避免影响下一条目 --> <xsl:accumulator-rule match="wd:Report_Entry" phase="end" select="()"/> </xsl:accumulator> <xsl:accumulator name="idmap" as="map(xs:string,xs:string)" initial-value="map{}" streamable="no"> <!-- 仅当calpersonids有有效值时,才写入map --> <xsl:accumulator-rule match="wd:Wiser_ID/text()" select="if (exists(accumulator-before('calpersonids')) and accumulator-before('calpersonids') != '') then map:put($value, accumulator-before('calpersonids'), normalize-space()) else $value"/> </xsl:accumulator> <xsl:template match="AggregatedData"> <xsl:apply-templates select="copy-of(cal:stack/cal:timeoff)"/> </xsl:template> <xsl:template match="cal:timeoff"> <cal:timeoff> <cal:wiserid><xsl:value-of select="accumulator-before('idmap')(cal:person)"/></cal:wiserid> <cal:key><xsl:value-of select="cal:key"/></cal:key> <cal:date><xsl:value-of select="cal:date"/></cal:date> <cal:timeofftype><xsl:value-of select="cal:timeofftype"/></cal:timeofftype> <cal:hours><xsl:value-of select="cal:hours"/></cal:hours> </cal:timeoff> </xsl:template> </xsl:stylesheet>
修改说明
- 调整
calpersonids累加器:- 类型改为
xs:string?允许空值,初始值设为()(空序列); - 匹配
wd:Person_Id节点而非文本节点,确保空节点也能获取空值; - 添加
phase="end"规则,在每个wd:Report_Entry处理完成后重置累加器,避免条目间值复用。
- 类型改为
- 优化
idmap累加器:- 增加条件判断,仅当
calpersonids存在且非空时,才将映射写入map,跳过空Person_Id的无效条目。
- 增加条件判断,仅当
- 对齐期望输出:移除了多余的
cal:personid节点。
内容的提问来源于stack exchange,提问作者Arthi Velloori
相关产品推荐
相关产品推荐

