More group slicing in XSLT
To continue on my previous example on slicing (I made the term up, by the way), today we'll do something a little different.
Let's say that instead of just inserting the slice elements and a few items, we want to copy the whole tree n times (where n is the number of slices), each time with a different slice of items. That includes elements beyond the things we're currently slicing.
So let's say we have this document, similar to the previous one.
<group groupattribute='1'>
<repeat-me>each time</repeat-me>
<item item='1'>1 <sub>text</sub> </item>
<item item='2'>2 <sub>text</sub> </item>
<item item='3'>3 <sub>text</sub> </item>
<item item='4'>4 <sub>text</sub> </item>
<item item='5'>5 <sub>text</sub> </item>
</group>
And we want to get this effect.
<root>
<group groupattribute="1">
<repeat-me>each time</repeat-me>
<item item="1">1 <sub>text</sub></item>
<item item="2">2 <sub>text</sub></item>
</group>
<group groupattribute="1">
<repeat-me>each time</repeat-me>
<item item="3">3 <sub>text</sub></item>
<item item="4">4 <sub>text</sub></item>
</group>
<group groupattribute="1">
<repeat-me>each time</repeat-me>
<item item="5">5 <sub>text</sub></item>
</group>
</root>
Here is a stylesheet that can accomplish this, using parameters to pass around the replacements nodes for each copy of the tree. As always, variations in the comments are welcome.
<xsl:stylesheet version='1.0' xmlns:xsl='https://www.w3.org/1999/XSL/Transform'>
<xsl:output method='xml' indent='yes'/>
<!-- Just copy everything. -->
<xsl:template match='@* | node()' mode='everything'>
<xsl:copy>
<xsl:apply-templates select='@* | node()' mode='everything'/>
</xsl:copy>
</xsl:template>
<!-- Copy everything but replace items with the replacement parameter. -->
<xsl:template match='@* | node()' mode='withreplacement'>
<xsl:param name='replacement' />
<xsl:copy>
<!-- Copy everything except item elements. -->
<xsl:apply-templates select='@* | node()[local-name() != "item"]' mode='withreplacement'>
<xsl:with-param name='replacement' select='$replacement' />
</xsl:apply-templates>
<!-- If this is where items are, apply the replacement node-set. -->
<xsl:if test='item'>
<xsl:apply-templates select='$replacement' mode='everything' />
</xsl:if>
</xsl:copy>
</xsl:template>
<!--
We matched the first of a slice of items.
Copy everything from the root, passing the current item
and its sibling(s) as the replacement.
-->
<xsl:template match='item' mode='firstofslice'>
<xsl:apply-templates select='/group' mode='withreplacement'>
<xsl:with-param
name='replacement'
select='. | following-sibling::item[1]'
/>
</xsl:apply-templates>
</xsl:template>
<!-- Match to root, and put everything under a single element. -->
<xsl:template match='/'>
<root>
<!-- Grab every second item to create a new group. -->
<xsl:apply-templates select='group/item[position() mod 2 = 1]' mode='firstofslice' />
</root>
</xsl:template>
</xsl:stylesheet>
Enjoy!
Comments
- Anonymous
June 05, 2009
PingBack from http://www.alvinashcraft.com/2009/06/05/dew-drop-june-5-2009/