Reputation: 9461
I have some XML that looks like this:
<root>
<node id="2_1" name="FOO" />
<node id="2_2" class="child" />
<node id="2_3" class="child" />
<node id="2_4" class="child" />
<node id="3_1" name="BAR" />
<node id="3_2" class="child" />
<node id="3_3" class="child" />
<node id="3_4" class="child" />
</root>
That is, a child
's parent node id
always shares the same prefix, but ends in _1
, and all child node suffixes start from 2 and increment.
I'm trying to produce output similar to:
<groups>
<group id="2_1">
<child id="2_2" />
<child id="2_3" />
<child id="2_4" />
</group>
<group id="3_1">
<child id="3_2" />
<child id="3_3" />
<child id="3_4" />
</group>
</groups>
I have this XSLT to get the groups, but I'm unsure how to select the children:
<xsl:template match="/">
<groups>
<xsl:for-each select="/root/node[@name]">
<group>
<xsl:attribute name="id">
<xsl:value-of select="@id" />
</xsl:attribute>
</group>
</xsl:for-each>
</groups>
</xsl:template>
Am I going about this the right way? I think I need a template that accepts a parameter, but I'm unsure how to assign that parameter the current group's id
value.
Upvotes: 0
Views: 641
Reputation: 70638
Actually, you don't need to go the full way with grouping here, despite my comment. If indeed the rule the group id always ends in _1
and the child elements end in higher number, you can simplify it a bit.
You would still create a key, but one that matches only
<xsl:key name="nodes" match="node" use="substring-before(@id, '_')" />
Then, if you start by selecting the _1
elements, you can get the child elements by using the key
<xsl:apply-templates
select="key('nodes', substring-before(@id, '_'))[substring-after(@id, '_') != '1']" />
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="nodes" match="node" use="substring-before(@id, '_')" />
<xsl:template match="root">
<groups>
<xsl:apply-templates select="node[substring-after(@id, '_') = '1']" />
</groups>
</xsl:template>
<xsl:template match="node[substring-after(@id, '_') = '1']">
<group id="{@id}">
<xsl:apply-templates select="key('nodes', substring-before(@id, '_'))[substring-after(@id, '_') != '1']" />
</group>
</xsl:template>
<xsl:template match="node">
<child id="{@id}">
</child>
</xsl:template>
</xsl:stylesheet>
Alternatively, if the "group" elements always have a name
attribute, and the child items don't, you could slightly simplify it to this...
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="nodes" match="node" use="substring-before(@id, '_')" />
<xsl:template match="root">
<groups>
<xsl:apply-templates select="node[@name]" />
</groups>
</xsl:template>
<xsl:template match="node[@name]">
<group id="{@id}">
<xsl:apply-templates select="key('nodes', substring-before(@id, '_'))[not(@name)]" />
</group>
</xsl:template>
<xsl:template match="node">
<child id="{@id}">
</child>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1