XSLT子节点复制至对应父节点问题求助
XSLT映射问题:匹配L24的MAT字段与ITEM的IDEN字段并移动节点
需求:编写XSLT,将ITEM段移动至对应的L24父节点(匹配L24的<MAT>字段值与ITEM的<IDEN>字段值),不匹配的ITEM段不保留。现有XSLT无法得到预期转换结果,以下是相关代码及预期效果:
输入XML
<?xml version="1.0" encoding="UTF-8"?> <HEADER> <CODI BEGIN="1"> <L20 SEGMENT="1"> <ELN>Value1</ELN> <L24 SEGMENT="1"> <MAT>12345</MAT> <field1>text </field1> <ITEM SEGMENT="1"> <IDEN>12345</IDEN> <Fields>00</Fields> </ITEM> <ITEM SEGMENT="1"> <IDEN>45678</IDEN> <Fields>10</Fields> </ITEM> <ITEM SEGMENT="1"> <IDEN>45678</IDEN> <Fields>11</Fields> </ITEM> <ITEM SEGMENT="1"> <IDEN>667788</IDEN> <Fields>12</Fields> </ITEM> <ITEM SEGMENT="1"> <IDEN>667788</IDEN> <Fields>13</Fields> </ITEM> <ITEM SEGMENT="1"> <IDEN>112233</IDEN> <Fields>20</Fields> </ITEM> <L19 SEGMENT="1"> <LF>Value2</LF> </L19> </L24> <L24 SEGMENT="1"> <MAT>45678</MAT> <field1>text1 </field1> <L19 SEGMENT="1"> <LF>Value2</LF> </L19> </L24> <L24 SEGMENT="1"> <MAT>60987</MAT> <field1>text2 </field1> <L19 SEGMENT="1"> <LF>Value2</LF> </L19> </L24> <L24 SEGMENT="1"> <MAT>667788</MAT> <field1>text3 </field1> <L19 SEGMENT="1"> <LF>Value2</LF> </L19> </L24> <L24 SEGMENT="1"> <MAT>112233</MAT> <field1>text4 </field1> <L19 SEGMENT="1"> <LF>Value2</LF> </L19> </L24> </L20> </CODI> </HEADER>
预期输出XML
<?xml version="1.0" encoding="UTF-8"?> <HEADER> <CODI BEGIN="1"> <L20 SEGMENT="1"> <ELN>Value1</ELN> <L24 SEGMENT="1"> <MAT>12345</MAT> <field1>text </field1> <ITEM SEGMENT="1"> <IDEN>12345</IDEN> <Fields>00</Fields> </ITEM> <L19 SEGMENT="1"> <LF>Value2</LF> </L19> </L24> <L24 SEGMENT="1"> <MAT>45678</MAT> <field1>text1 </field1> <ITEM SEGMENT="1"> <IDEN>45678</IDEN> <Fields>10</Fields> </ITEM> <ITEM SEGMENT="1"> <IDEN>45678</IDEN> <Fields>11</Fields> </ITEM> <L19 SEGMENT="1"> <LF>Value2</LF> </L19> </L24> <L24 SEGMENT="1"> <MAT>60987</MAT> <field1>text2 </field1> <L19 SEGMENT="1"> <LF>Value2</LF> </L19> </L24> <L24 SEGMENT="1"> <MAT>667788</MAT> <field1>text3 </field1> <ITEM SEGMENT="1"> <IDEN>667788</IDEN> <Fields>12</Fields> </ITEM> <ITEM SEGMENT="1"> <IDEN>667788</IDEN> <Fields>13</Fields> </ITEM> <L19 SEGMENT="1"> <LF>Value2</LF> </L19> </L24> <L24 SEGMENT="1"> <MAT>112233</MAT> <field1>text4 </field1> <ITEM SEGMENT="1"> <IDEN>112233</IDEN> <Fields>20</Fields> </ITEM> <L19 SEGMENT="1"> <LF>Value2</LF> </L19> </L24> </L20> </CODI> </HEADER>
当前使用的XSLT
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="IDEN-by-key" match="//ITEM" use="@IDEN" /> <!-- Identity template to copy all nodes as-is --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template match="//L24"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> <xsl:for-each select="('IDEN-by-key', @IDEN)"> <xsl:copy-of select="."/> </xsl:for-each> </xsl:copy> </xsl:template> </xsl:stylesheet>
问题分析与修正方案
原XSLT存在两处关键错误:
- 键定义错误:
<xsl:key>中use="@IDEN"错误,IDEN是ITEM的子元素而非属性,应改为use="IDEN"(取元素的字符串值)。 - L24模板逻辑错误:原模板中的
<xsl:for-each>写法完全不符合key的调用逻辑,且未过滤原有的ITEM节点,导致所有原始ITEM会被保留,同时无法正确插入匹配的节点。
修正后的XSLT如下:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 定义键:用ITEM的IDEN元素值作为键值 --> <xsl:key name="item-by-iden" match="ITEM" use="IDEN"/> <!-- 身份模板:默认复制所有节点 --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- 过滤所有原始ITEM节点,避免保留多余节点 --> <xsl:template match="ITEM"/> <!-- 处理L24节点:在自身内容后插入匹配的ITEM --> <xsl:template match="L24"> <xsl:copy> <!-- 复制L24自身的属性和子节点(除了ITEM) --> <xsl:apply-templates select="@* | node()"/> <!-- 查找所有IDEN等于当前L24的MAT值的ITEM,复制到当前L24下 --> <xsl:copy-of select="key('item-by-iden', MAT)"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
修正说明
- 键
item-by-iden正确绑定ITEM的IDEN元素值,用于快速查找匹配节点。 - 添加
match="ITEM"的模板,不输出任何内容,过滤掉所有原始的ITEM节点。 - L24模板中,先复制自身的所有内容,再通过
key()函数找到所有与当前L24的MAT值匹配的ITEM,将这些节点复制到L24节点内部,位置在原有子节点之后。
此代码可实现需求中的节点移动与匹配逻辑,输出结果与预期一致。
内容的提问来源于stack exchange,提问作者CHAR
相关产品推荐
相关产品推荐

