这是另一种 XSLT 2.0 样式表,我认为它是最简单但通用的解决方案。
这种转变...
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="sep" select="' > '" />
<xsl:template match="categories" name="cat-grouper">
<xsl:param name="cat-strs" as="xs:string*" select="category" />
<categories>
<xsl:for-each-group select="$cat-strs[contains(.,$sep)]" group-by="substring-before(.,$sep)">
<category name="{current-grouping-key()}">
<xsl:call-template name="cat-grouper">
<xsl:with-param name="cat-strs" select="for $s in current-group() return substring-after($s,$sep)" />
</xsl:call-template>
</category>
</xsl:for-each-group>
<xsl:for-each-group select="$cat-strs[not(contains(.,$sep))]" group-by=".">
<category name="{current-grouping-key()}" />
</xsl:for-each-group>
</categories>
</xsl:template>
</xsl:stylesheet>
...应用于以下输入(改编自 OP 的示例输入,以解决他与输入中两次出现有关的评论<category>För honom > Badkläder</category>
)...
<categories>
<category>För henne > Ansikte</category>
<category>För henne > Pumps & klackskor</category>
<category>För henne > Stövlar & stövletter</category>
<category>För honom > Badkläder</category>
<category>För honom > Badkläder</category>
</categories>
...产量...
<categories>
<category name="För henne">
<categories>
<category name="Ansikte"/>
<category name="Pumps & klackskor"/>
<category name="Stövlar & stövletter"/>
</categories>
</category>
<category name="För honom">
<categories>
<category name="Badkläder"/>
</categories>
</category>
</categories>
笔记
通过双重使用唯一的模板可以获得一些简单性。它既是categories
根元素的匹配模板,也是处理类别字符串列表的命名模板。
另一个
这是另一种替代解决方案。如果更喜欢我提供给你的第一个,但值得看看这个。使用 tokenize() 的类似用法,您可能会认为这只是对 Dimitre 解决方案的调整。
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="sep" select="' > '" />
<xsl:template match="categories" name="cat-grouper">
<xsl:param name="cat-strs" as="xs:string*" select="category" />
<categories>
<xsl:for-each-group select="$cat-strs" group-by="tokenize(.,$sep)[1]">
<category name="{current-grouping-key()}">
<xsl:variable name="subcats" select="
for $s in current-group()[contains(.,$sep)]
return substring-after($s,$sep)" />
<xsl:if test="exists($subcats)">
<xsl:call-template name="cat-grouper">
<xsl:with-param name="cat-strs" select="$subcats" />
</xsl:call-template>
</xsl:if>
</category>
</xsl:for-each-group>
</categories>
</xsl:template>
</xsl:stylesheet>
附录
这是将我的第一个解决方案应用于 Dimitre 的输入文档的结果输出。我要求 OP 建议这是否是对他的转换规则的正确解释。这是我的理解,但也许我理解错了?
<categories>
<category name="A">
<categories>
<category name="AB1">
<categories>
<category name="AB1C1"/>
</categories>
</category>
<category name="AB2">
<categories>
<category name="AB2C1">
<categories>
<category name="AB2C1D1"/>
<category name="AB2C3"/>
</categories>
</category>
<category name="AB2C1"/>
</categories>
</category>
</categories>
</category>
<category name="F">
<categories>
<category name="XY1">
<categories>
<category name="XY1Z1"/>
</categories>
</category>
<category name="XY2">
<categories>
<category name="XY2Z1">
<categories>
<category name="XY2Z1T1"/>
<category name="XY2Z3"/>
</categories>
</category>
<category name="XY2Z1"/>
</categories>
</category>
</categories>
</category>
</categories>