如何在XSLT3.0中正确使用map:put()更新XML数据映射
XSLT 3.0 映射更新问题解决及实现方案
核心问题定位
你遇到的map:put()问题,根源在于XSLT 3.0中的map是不可变对象:map:put()不会修改原有映射,而是返回一个包含新键值对的全新映射。如果没有将这个新映射传递到后续处理逻辑中,就无法维护递增的计数状态。
需求实现方案
针对你的业务规则,我们可以用xsl:iterate(XSLT 3.0新增的状态迭代工具)结合不可变map来实现,步骤如下:
1. 预处理资产查找映射
先把Asset Information文件转换成键为Asset_Identifier、值为Location_ID的map,方便快速查询:
<xsl:variable name="asset-lookup-map" as="map(xs:string, xs:string)"> <xsl:map> <xsl:for-each select="document('asset-information.xml')//Asset"> <xsl:map-entry key="string(Asset_Identifier)" select="string(Location_ID)"/> </xsl:for-each> </xsl:map> </xsl:variable>
2. 迭代处理Journal Lines并维护计数映射
用xsl:iterate逐条处理Journal Lines记录,每次迭代更新计数映射并传递到下一轮:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:map="http://www.w3.org/2005/xpath-functions/map"> <xsl:output method="xml" indent="yes"/> <xsl:variable name="asset-lookup-map" as="map(xs:string, xs:string)"> <xsl:map> <xsl:for-each select="document('asset-information.xml')//Asset"> <xsl:map-entry key="string(Asset_Identifier)" select="string(Location_ID)"/> </xsl:for-each> </xsl:map> </xsl:variable> <xsl:template match="/"> <Output> <xsl:iterate select="document('journal-lines.xml')//JournalLine"> <!-- 初始状态:空计数映射 --> <xsl:param name="counter-map" as="map(xs:string, xs:integer)" select="map{}"/> <xsl:variable name="current-asset-id" select="string(Asset_Identifier)"/> <!-- 获取当前计数,不存在则返回空序列 --> <xsl:variable name="current-count" select="$counter-map($current-asset-id, ())"/> <!-- 计算新的Asset_Identifier_Extension值 --> <xsl:variable name="new-extension" as="xs:integer"> <xsl:choose> <xsl:when test="empty($current-count)"> <!-- 首次处理:存在资产信息则设为1,否则设为0 --> <xsl:choose> <xsl:when test="exists($asset-lookup-map($current-asset-id))">1</xsl:when> <xsl:otherwise>0</xsl:otherwise> </xsl:choose> </xsl:when> <xsl:otherwise> <!-- 后续处理:在当前计数基础上加1 --> <xsl:value-of select="$current-count + 1"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <!-- 获取Location_ID,不存在则返回空字符串 --> <xsl:variable name="location-id" select="$asset-lookup-map($current-asset-id, '')"/> <!-- 生成输出记录 --> <JournalLine> <Asset_Identifier><xsl:value-of select="$current-asset-id"/></Asset_Identifier> <Asset_Identifier_Extension><xsl:value-of select="$new-extension"/></Asset_Identifier_Extension> <xsl:if test="$location-id ne ''"> <Location_ID><xsl:value-of select="$location-id"/></Location_ID> </xsl:if> <!-- 保留原记录的其他字段 --> <xsl:copy-of select="* except Asset_Identifier"/> </JournalLine> <!-- 更新计数映射,传递到下一次迭代 --> <xsl:next-iteration> <xsl:with-param name="counter-map" select="map:put($counter-map, $current-asset-id, $new-extension)"/> </xsl:next-iteration> </xsl:iterate> </Output> </xsl:template> </xsl:stylesheet>
关键细节说明
- 不可变map的正确使用:每次调用
map:put()后,必须将返回的新映射通过xsl:next-iteration传递给下一轮迭代,否则计数状态无法保留。 - 状态维护:
xsl:iterate是XSLT 3.0中处理逐次状态更新的最优方式,比递归调用更简洁易读。 - 默认值处理:使用
map($key, 默认值)语法处理键不存在的场景,避免空指针错误。
内容的提问来源于stack exchange,提问作者Raj
相关产品推荐
相关产品推荐

