Reputation: 1101
My input xml below has many 'a' elements under subroot tag. Whichever 'a' group, having a keyid, not starts with 'ab', should come under the preceding 'a' element, which starts with 'ab'(keyid element). The output will have a new tag 'subdetail'. Here is my desired output. If there is no subgroup, the subdetail elements will be absent in the output. input xml
<Root>
<subroot>
<a>
<keyid>ab123456</keyid>
<detail>hi</detail>
</a>
<a>
<keyid>09876</keyid>
<detail>undermain1</detail>
</a>
<a>
<keyid>087564</keyid>
<detail>undermain2</detail>
</a>
<a>
<keyid>ab4567</keyid>
<detail>hi</detail>
</a>
<a>
<keyid>056432</keyid>
<detail>undermain1</detail>
</a>
</subroot>
</Root>
desired output
<Root>
<subroot>
<a>
<keyid>ab123456</keyid>
<detail>hi</detail>
<subdetail>
<detail>undermain1</detail>
</subdetail>
<subdetail>
<detail>undermain2</detail>
</subdetail>
</a>
<a>
<keyid>ab4567</keyid>
<detail>hi</detail>
<subdetail>
<detail>undermain1</detail>
</subdetail>
</a>
</subroot>
</Root>
Upvotes: 0
Views: 95
Reputation: 70648
This can be achieved by creating a key to group a elements by the most preceding a element with an 'ab' key
<xsl:key
name="a"
match="a[not(starts-with(keyid, 'ab'))]"
use="generate-id(preceding-sibling::a[starts-with(keyid, 'ab')][1])" />
Then when you match the a elements with an 'ab' key, you can get the other a elements as follows
<xsl:apply-templates select="key('a', generate-id())" />
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="a" match="a[not(starts-with(keyid, 'ab'))]" use="generate-id(preceding-sibling::a[starts-with(keyid, 'ab')][1])" />
<xsl:template match="subroot">
<xsl:apply-templates select="a[starts-with(keyid, 'ab')]" />
</xsl:template>
<xsl:template match="a[starts-with(keyid, 'ab')]">
<a>
<xsl:apply-templates select="@*|node()" />
<xsl:apply-templates select="key('a', generate-id())" />
</a>
</xsl:template>
<xsl:template match="a">
<subdetail>
<xsl:copy-of select="detail" />
</subdetail>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to the sample XML, the following is output
<Root>
<a>
<keyid>ab123456</keyid>
<detail>hi</detail>
<subdetail>
<detail>undermain1</detail>
</subdetail>
<subdetail>
<detail>undermain2</detail>
</subdetail>
</a>
<a>
<keyid>ab4567</keyid>
<detail>hi</detail>
<subdetail>
<detail>undermain1</detail>
</subdetail>
</a>
</Root>
Upvotes: 1