我想使用 XSLT 2.0 将以下平面 XML 结构转换为嵌套/分层 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>
使用每个元素的parent-id 属性我想创建一个嵌套层次结构。如果一个元素的parent-id属性值与另一个元素的id属性匹配 - 那么它就成为该元素的子元素。
这是我想要实现的目标:
<?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>
我已经研究过使用键和for-each-group,但我不知道如何应用它们以及必要的递归。任何帮助将不胜感激。
我有以下内容:
<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>
经过 Martin Honnen 的敏锐观察,现在可以使用了! - 将 id 属性值从
prod_family
更正为 product_family
。
正如我在评论中所说,递归是通过 apply-templates 完成的:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:key name="child" match="product" use="@parent-id"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="productlist">
<xsl:copy>
<xsl:apply-templates select="key('child', 'root')"/>
</xsl:copy>
</xsl:template>
<xsl:template match="product">
<xsl:copy>
<xsl:apply-templates select="@*, node(), key('child', @id)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>