Reputation: 565
I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<Container>
<Item type="forest"/>
<Item type="tree"/>
<Item type="branch"/>
<Item type="leaf"/>
<Item type="branch"/>
<Item type="leaf"/>
<Item type="leaf"/>
<Item type="tree"/>
<Item type="branch"/>
<Item type="branch"/>
<Item type="leaf"/>
<Item type="forest"/>
<Item type="tree"/>
<Item type="branch"/>
<Item type="leaf"/>
</Container>
The data represents the following structure:
For the given XML:
I want to process the data in XSLT similar to the following, had the type attributes been tag names:
<?xml version='1.0' encoding='utf-8'?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match='/'>
<xsl:for-each select="/Container/forest">
<xsl:for-each select="tree">
<xsl:for-each select="branch">
<xsl:for-each select="leaf">
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
How can this be achieved?
Upvotes: 1
Views: 92
Reputation: 167436
The existing answer has the grouping nicely spelled out but of course the question kind of asks for recursion:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:output indent="yes"/>
<xsl:param name="levels" as="xs:string*" select="'forest', 'tree', 'branch', 'leaf'"/>
<xsl:function name="mf:group" as="element()*">
<xsl:param name="items" as="element()*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:for-each-group select="$items" group-starting-with="Item[@type = $levels[$level]]">
<xsl:element name="{@type}">
<xsl:sequence select="mf:group(tail(current-group()), $level + 1)"/>
</xsl:element>
</xsl:for-each-group>
</xsl:function>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="Container">
<root>
<xsl:sequence select="mf:group(*, 1)"/>
</root>
</xsl:template>
</xsl:stylesheet>
Upvotes: 2
Reputation: 116959
Consider the following example:
XML
<Container>
<Item type="forest" id="1"/>
<Item type="tree" id="2"/>
<Item type="branch" id="3"/>
<Item type="leaf" id="4"/>
<Item type="branch" id="5"/>
<Item type="leaf" id="6"/>
<Item type="leaf" id="7"/>
<Item type="tree" id="8"/>
<Item type="branch" id="9"/>
<Item type="branch" id="10"/>
<Item type="leaf" id="11"/>
<Item type="forest" id="12"/>
<Item type="tree" id="13"/>
<Item type="branch" id="14"/>
<Item type="leaf" id="15"/>
</Container>
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"/>
<xsl:template match="/Container">
<root>
<xsl:for-each-group select="Item" group-starting-with="Item[@type='forest']">
<forest id="{@id}">
<xsl:for-each-group select="current-group()[position() gt 1]" group-starting-with="Item[@type='tree']">
<tree id="{@id}">
<xsl:for-each-group select="current-group()[position() gt 1]" group-starting-with="Item[@type='branch']">
<branch id="{@id}">
<xsl:for-each select="current-group()[position() gt 1]">
<leaf id="{@id}"/>
</xsl:for-each>
</branch>
</xsl:for-each-group>
</tree>
</xsl:for-each-group>
</forest>
</xsl:for-each-group>
</root>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="utf-8"?>
<root>
<forest id="1">
<tree id="2">
<branch id="3">
<leaf id="4"/>
</branch>
<branch id="5">
<leaf id="6"/>
<leaf id="7"/>
</branch>
</tree>
<tree id="8">
<branch id="9"/>
<branch id="10">
<leaf id="11"/>
</branch>
</tree>
</forest>
<forest id="12">
<tree id="13">
<branch id="14">
<leaf id="15"/>
</branch>
</tree>
</forest>
</root>
Upvotes: 3