基于XSLT 2.0实现扁平XML到嵌套层级XML的结构转换
用XSLT 2.0实现扁平XML到嵌套层级XML的转换
需求说明
需要将扁平结构的XML转换为嵌套层级结构,核心逻辑是利用每个<product>元素的parent-id属性构建层级关系:当某一<product>的parent-id属性值与另一<product>的id属性值匹配时,前者作为子元素嵌套在后者内部。
此前尝试结合key和for-each-group实现,但未能成功结合递归逻辑。目前已有一段经修正(将prod_family改为product_family)的XSLT 1.0代码可运行,现寻求更简洁高效的XSLT 2.0实现方案。
输入XML
<?xml version="1.0" encoding="UTF-8"?> <products> <productlist> <product id="prod_lmo" parent-id="product_family"><prodname>LMO</prodname></product> <product id="prod_sdf" parent-id="product_family"><prodname>SDF</prodname></product> <product id="prod_sdfg" parent-id="prod_sdf"><prodname>SDFg</prodname></product> <product id="prod_abc123" parent-id="prod_abc"><prodname>ABC-123</prodname></product> <product id="prod_sdfh" parent-id="prod_sdfg"><prodname>SDF-h</prodname></product> <product id="prod_family" parent-id="root"><prodname>Product Family</prodname></product> <product id="prod_sdfj" parent-id="prod_sdfh"><prodname>SDF-j</prodname></product> <product id="prod_lmao" parent-id="prod_lmo"><prodname>LMAO</prodname></product> <product id="prod_elo" parent-id="product_family"><prodname>ELO</prodname></product> <product id="prod_lmfao" parent-id="prod_lmo"><prodname>LMFAO</prodname></product> <product id="prod_abc" parent-id="product_family"><prodname>ABC</prodname></product> </productlist> </products>
期望输出XML
<?xml version="1.0" encoding="UTF-8"?> <products> <productlist> <product id="product_family" parent-id="root"><prodname>Product Family</prodname> <product id="prod_elo" parent-id="product_family"><prodname>ELO</prodname></product> <product id="prod_lmo" parent-id="product_family"><prodname>LMO</prodname> <product id="prod_lmao" parent-id="prod_lmo"><prodname>LMAO</prodname></product> <product id="prod_lmfao" parent-id="prod_lmo"><prodname>LMFAO</prodname></product> </product> <product id="prod_sdf" parent-id="product_family"><prodname>SDF</prodname> <product id="prod_sdfg" parent-id="prod_sdf"><prodname>SDFg</prodname> <product id="prod_sdfh" parent-id="prod_sdfg"><prodname>SDF-h</prodname> <product id="prod_sdfj" parent-id="prod_sdfh"><prodname>SDF-j</prodname></product> </product> </product> </product> <product id="prod_abc" parent-id="product_family"><prodname>ABC</prodname> <product id="prod_abc123" parent-id="prod_abc"><prodname>ABC-123</prodname></product> </product> </product> </productlist> </products>
现有XSLT 1.0代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:key name="children" match="product" use="@parentid"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="products/productlist"> <products> <productlist> <xsl:for-each select="product[@parentid='root']"> <product> <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute> <xsl:attribute name="pid"><xsl:value-of select="@parentid"/></xsl:attribute> <xsl:copy-of select="prodname"/> <xsl:for-each select="key('children', @id)"> <product> <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute> <xsl:attribute name="pid"><xsl:value-of select="@parentid"/></xsl:attribute> <xsl:copy-of select="prodname"/> <xsl:for-each select="key('children', @id)"> <product> <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute> <xsl:attribute name="pid"><xsl:value-of select="@parentid"/></xsl:attribute> <xsl:copy-of select="prodname"/> <xsl:for-each select="key('children', @id)"> <product> <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute> <xsl:attribute name="pid"><xsl:value-of select="@parentid"/></xsl:attribute> <xsl:copy-of select="prodname"/> </product> </xsl:for-each> </product> </xsl:for-each> </product> </xsl:for-each> </product> </xsl:for-each> </productlist> </products> </xsl:template> </xsl:stylesheet>
XSLT 2.0解决方案
实现思路
利用<xsl:key>建立parent-id到对应子<product>元素的映射,通过递归模板处理任意深度的层级结构,无需硬编码层级数,比XSLT 1.0方案更灵活、易维护。
完整XSLT 2.0代码
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <!-- 建立parent-id到子product元素的索引 --> <xsl:key name="child-products" match="product" use="@parent-id"/> <!-- 身份模板:复制所有未被特殊模板匹配的节点 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 处理productlist节点,只保留根节点(parent-id为root的product) --> <xsl:template match="productlist"> <xsl:copy> <xsl:apply-templates select="key('child-products', 'root')"/> </xsl:copy> </xsl:template> <!-- 处理product节点,递归添加子元素 --> <xsl:template match="product"> <xsl:copy> <!-- 复制当前product的所有属性和子节点 --> <xsl:apply-templates select="@*|node()"/> <!-- 递归处理当前product的所有子product --> <xsl:apply-templates select="key('child-products', @id)"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
代码说明
- 索引建立:
<xsl:key>将所有<product>按parent-id分组,快速查找指定父ID对应的子元素; - 身份模板:默认复制所有节点,保证XML结构的完整性;
- productlist处理:仅筛选出
parent-id="root"的根节点,作为层级结构的起点; - 递归模板:处理每个
<product>时,先复制自身内容,再递归处理其所有子元素,自动适配任意深度的层级。
内容的提问来源于stack exchange,提问作者ecklectique
相关产品推荐
相关产品推荐

