XSLT/XPath 2.0实现XML属性复制遇1对多场景报错求方案
解决XSLT 1对多场景下XTTE3180错误的方案
问题根源
XTTE3180错误的核心原因是:xsl:copy的@select属性只能接收单个节点或空序列,而你在1对多场景下的XPath表达式返回了多个<Y>节点,违反了类型约束。
直接解决思路
不要直接在xsl:copy的select里返回多节点序列,改用遍历+逐个复制的方式处理每个匹配的<Y>元素。
具体实现示例
假设你的两个XML结构如下:
XML1(源数据)
<Root1> <X id="x001"> <Y key="y001"/> <Y key="y002"/> </X> </Root1>
XML2(目标文档)
<Root2> <X id="x001"> <!-- 需要插入XML1中对应id的所有Y节点 --> </X> </Root2>
错误写法(触发XTTE3180)
<!-- 错误:select返回多个Y节点,xsl:copy无法处理 --> <xsl:copy select="document('xml1.xml')/Root1/X[@id=current()/@id]/Y"/>
正确写法1:用xsl:for-each遍历复制
<xsl:template match="/Root2/X"> <xsl:copy> <!-- 保留目标X节点的原有属性 --> <xsl:copy-of select="@*"/> <!-- 遍历源文档中匹配的所有Y节点,逐个复制 --> <xsl:for-each select="document('xml1.xml')/Root1/X[@id=current()/@id]/Y"> <xsl:copy-of select="."/> </xsl:for-each> <!-- 保留目标X节点的原有子节点(如果需要) --> <xsl:apply-templates/> </xsl:copy> </xsl:template> <!-- 默认模板:复制其他节点结构 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template>
正确写法2:用模板匹配实现(更符合XSLT设计思想)
<!-- 匹配目标文档的X节点 --> <xsl:template match="/Root2/X"> <xsl:copy> <xsl:copy-of select="@*"/> <!-- 调用模板处理所有匹配的Y节点 --> <xsl:apply-templates select="document('xml1.xml')/Root1/X[@id=current()/@id]/Y"/> <xsl:apply-templates/> </xsl:copy> </xsl:template> <!-- 匹配源文档的Y节点,直接复制 --> <xsl:template match="document('xml1.xml')/Root1/X/Y"> <xsl:copy-of select="."/> </xsl:template> <!-- 默认模板 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template>
额外注意事项
- 如果只需要复制
<Y>的key属性而非整个节点,可调整XPath为select="document('xml1.xml')/Root1/X[@id=current()/@id]/Y/@key",并结合xsl:attribute逐个设置。 - 跨文档处理时,务必用
document()函数正确加载源XML,避免上下文混乱。 - 使用
current()而非.来引用当前目标节点(比如XML2的<X>),防止XPath上下文切换到源文档节点。
内容的提问来源于stack exchange,提问作者Rao E
相关产品推荐
相关产品推荐

