如何在XSLT中关联具有相同定义及映射关系的ID?
问题1:XSLT中关联相同定义的ID
当多个元素共享同一个ID值时,最直接的方式是用XSLT的<xsl:key>建立索引,通过key()函数快速查找所有匹配该ID的节点。
举个实际场景:假设你的XML结构如下:
<root> <product id="P001" name="Laptop"/> <order-item product-id="P001" quantity="2"/> <inventory product-id="P001" stock="15"/> </root>
你可以在XSLT中定义key:
<xsl:key name="product-by-id" match="product" use="@id"/>
之后在处理order-item或inventory时,就能通过key('product-by-id', @product-id)获取对应的<product>节点,实现ID关联。如果有多个节点匹配同一个ID,key()会返回所有匹配的节点集合,你可以用for-each或者其他方式遍历处理。
问题2:处理链式ID映射场景
针对你给出的ID映射规则:
id=1 => id=2
id=2 => id=null
id=3 => id=2
id=4 => id=3
这种链式映射需要递归解析最终的目标ID(直到遇到null或循环),可以按以下步骤实现:
步骤1:定义映射数据
先在XSLT中嵌入映射规则,比如用一个内部的XML片段:
<xsl:variable name="id-mappings"> <map from="1" to="2"/> <map from="2" to=""/> <!-- 对应null --> <map from="3" to="2"/> <map from="4" to="3"/> </xsl:variable> <!-- 将变量转为节点集(XSLT 1.0需要,XSLT 2.0+可省略) --> <xsl:variable name="mapping-nodes" select="document('')//xsl:variable[@name='id-mappings']/*"/>
步骤2:建立映射索引
用<xsl:key>关联映射的from和to值:
<xsl:key name="id-map" match="map" use="@from"/>
步骤3:递归解析映射(XSLT 3.0+ 用函数)
如果用XSLT 3.0,写一个递归函数来解析最终ID:
<xsl:function name="my:resolve-final-id" as="xs:string?"> <xsl:param name="current-id" as="xs:string"/> <!-- 获取当前ID的映射值 --> <xsl:variable name="mapped-id" select="key('id-map', $current-id, $mapping-nodes)/@to"/> <xsl:choose> <!-- 如果映射值存在且不是自身(避免循环),继续递归 --> <xsl:when test="$mapped-id and $mapped-id != $current-id"> <xsl:sequence select="my:resolve-final-id($mapped-id)"/> </xsl:when> <!-- 否则返回最终结果(空值对应null) --> <xsl:otherwise> <xsl:sequence select="$mapped-id"/> </xsl:otherwise> </xsl:choose> </xsl:function>
调用示例:my:resolve-final-id('4')会返回2,my:resolve-final-id('2')返回空字符串(对应你的null)。
步骤3(兼容XSLT 1.0:用递归模板)
如果需要兼容XSLT 1.0,改用递归模板:
<xsl:template name="resolve-final-id"> <xsl:param name="current-id"/> <xsl:variable name="mapped-id" select="key('id-map', $current-id, $mapping-nodes)/@to"/> <xsl:if test="$mapped-id and $mapped-id != $current-id"> <xsl:call-template name="resolve-final-id"> <xsl:with-param name="current-id" select="$mapped-id"/> </xsl:call-template> </xsl:if> <xsl:if test="not($mapped-id) or $mapped-id = $current-id"> <xsl:value-of select="$mapped-id"/> </xsl:if> </xsl:template>
调用示例:
<xsl:call-template name="resolve-final-id"> <xsl:with-param name="current-id" select="'1'"/> </xsl:call-template>
返回结果为2。
内容的提问来源于stack exchange,提问作者anon
相关产品推荐
相关产品推荐

