求助:使用XSLT对相同MAT值的ROOT段按FIELD3排序
XSLT分组排序问题:仅对相同MAT值的ROOT段按FIELD3排序,其余节点保持原位置
需求说明
我需要实现以下XSLT转换逻辑:
- 当XML中存在多个
<ROOT>节点且它们的<MAT>子元素值相同时,将这一组<ROOT>节点按<FIELD3>子元素的值排序 - 所有其他节点(包括MAT值唯一的
<ROOT>节点、非<ROOT>节点)均保持原有位置和结构不变
输入XML
<?xml version="1.0" encoding="UTF-8"?> <HEADER> <DOC BEGIN="1"> <EDI_40> <Field1>123</Field1> </EDI_40> <E_E20> <ROOT SEGMENT="1"> <POS>000020</POS> <MAT>1234</MAT> <FIELD3>4199</FIELD3> <QUAN>3420</QUAN> <CHILD SEGMENT="1"> <POS>000010</POS> <PQ>UN</PQ> </CHILD> </ROOT> <ROOT SEGMENT="1"> <POS>000010</POS> <MAT>123456</MAT> <FIELD3>4199BI015</FIELD3> <QUAN>3420</QUAN> <CHILD SEGMENT="1"> <POS>000010</POS> <PQ>UN1</PQ> </CHILD> </ROOT> <ROOT SEGMENT="1"> <POS>000010</POS> <MAT>123456</MAT> <FIELD3>4184AI015</FIELD3> <QUAN>1026</QUAN> <CHILD SEGMENT="1"> <POS>000010</POS> <PQ>UN2</PQ> </CHILD> <CHILD SEGMENT="1"> <POS>000010</POS> <PQ>UN2</PQ> </CHILD> </ROOT> <ROOT SEGMENT="1"> <POS>000010</POS> <MAT>123456</MAT> <FIELD3>4200AI015</FIELD3> <QUAN>1197</QUAN> <CHILD SEGMENT="1"> <POS>000010</POS> <PQ>UN3</PQ> </CHILD> </ROOT> <ROOT SEGMENT="1"> <POS>000030</POS> <MAT>123</MAT> <FIELD3>4200AI</FIELD3> <QUAN>1197</QUAN> <CHILD SEGMENT="1"> <POS>000010</POS> <PQ>UN</PQ> </CHILD> </ROOT> </E_E20> </DOC> </HEADER>
预期输出XML
<?xml version="1.0" encoding="UTF-8"?> <HEADER> <DOC BEGIN="1"> <EDI_40> <Field1>123</Field1> </EDI_40> <E_E20> <ROOT SEGMENT="1"> <POS>000020</POS> <MAT>1234</MAT> <FIELD3>4199</FIELD3> <QUAN>3420</QUAN> <CHILD SEGMENT="1"> <POS>000010</POS> <PQ>UN</PQ> </CHILD> </ROOT> <ROOT SEGMENT="1"> <POS>000010</POS> <MAT>123456</MAT> <FIELD3>4184AI015</FIELD3> <QUAN>1026</QUAN> <CHILD SEGMENT="1"> <POS>000010</POS> <PQ>UN2</PQ> </CHILD> <CHILD SEGMENT="1"> <POS>000010</POS> <PQ>UN2</PQ> </CHILD> </ROOT> <ROOT SEGMENT="1"> <POS>000010</POS> <MAT>123456</MAT> <FIELD3>4199BI015</FIELD3> <QUAN>3420</QUAN> <CHILD SEGMENT="1"> <POS>000010</POS> <PQ>UN1</PQ> </CHILD> </ROOT> <ROOT SEGMENT="1"> <POS>000010</POS> <MAT>123456</MAT> <FIELD3>4200AI015</FIELD3> <QUAN>1197</QUAN> <CHILD SEGMENT="1"> <POS>000010</POS> <PQ>UN3</PQ> </CHILD> </ROOT> <ROOT SEGMENT="1"> <POS>000030</POS> <MAT>123</MAT> <FIELD3>4200AI</FIELD3> <QUAN>1197</QUAN> <CHILD SEGMENT="1"> <POS>000010</POS> <PQ>UN</PQ> </CHILD> </ROOT> </E_E20> </DOC> </HEADER>
我使用的错误XSLT
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="//ROOT"> <ROOT> <xsl:for-each select="distinct-values(//ROOT/@MAT)"> <xsl:sort select="." data-type="number"/> <xsl:apply-templates select="//ROOT/FIELD3[@MAT = current()]"/> </xsl:for-each> </ROOT> </xsl:template> <xsl:template match="HEADER"> <xsl:copy/> </xsl:template> </xsl:stylesheet>
错误分析
- 模板匹配逻辑错误:匹配
//ROOT会导致每个<ROOT>节点被重复处理,完全偏离需求逻辑 - 节点选择错误:
MAT是<ROOT>的子元素而非属性,原代码中//ROOT/@MAT、//ROOT/FIELD3[@MAT = current()]的写法完全错误 - 节点复制不完整:匹配
HEADER的模板仅执行<xsl:copy/>,未复制任何子节点,导致大部分内容丢失
正确的XSLT解决方案
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 身份模板:默认复制所有节点和属性,保证非目标节点的结构不变 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 专门处理E_E20节点,对其子节点进行分组排序 --> <xsl:template match="E_E20"> <xsl:copy> <!-- 遍历E_E20的所有子节点,按规则分组 --> <xsl:for-each-group select="*" group-by="if (self::ROOT) then MAT else generate-id()"> <!-- 按组中第一个元素的原始位置排序,保证整体顺序与原文档一致 --> <xsl:sort select="index-of(current()/*, .)" data-type="number"/> <xsl:choose> <!-- 处理ROOT节点分组:组内按FIELD3排序后输出 --> <xsl:when test="self::ROOT"> <xsl:apply-templates select="current-group()"> <xsl:sort select="FIELD3"/> </xsl:apply-templates> </xsl:when> <!-- 非ROOT节点直接输出,保持原位置 --> <xsl:otherwise> <xsl:apply-templates select="current-group()"/> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </xsl:copy> </xsl:template> </xsl:stylesheet>
代码说明
- 身份模板:作为基础模板,负责复制所有未被特殊模板匹配的节点和属性,确保原XML的大部分结构和内容保持不变
- E_E20节点处理:
- 对
<E_E20>的所有子节点分组:<ROOT>节点按<MAT>值分组,非<ROOT>节点每个单独成组(用generate-id()保证唯一性) - 按组的原始出现顺序排序,确保整体结构与原文档一致
- 对
<ROOT>分组内的节点,按<FIELD3>的值排序后输出;非<ROOT>分组直接输出,保持原位置
- 对
- 兼容性:基于XSLT 2.0语法,需使用支持XSLT 2.0的处理器(如Saxon)
内容的提问来源于stack exchange,提问作者Anu
相关产品推荐
相关产品推荐

