Check for new replies
Thread Rating:
  • 84 Vote(s) - 3.35 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Grouping in XSLT-1.0 using preceding-sibling axis
#1
XSLT-1.0 doesn't have the grouping capability as that of XSLT-2.0 but there are many ways to achieve the desired.
Let's consider the following simple XML with multiple <<item>> elements in <<items>>(root element) that have @type that is repeating among other <<item>> elements.

Code:
<items>
    <item type="A">1</item>
    <item type="B">2</item>
    <item type="C">3</item>
    <item type="B">4</item>
    <item type="B">5</item>
    <item type="C">6</item>
    <item type="A">7</item>
</items>

If I want all the <<item>> elements having the same @type to get grouped as following output:

Code:
<?xml version="1.0" encoding="utf-8"?>
<items>
   <item type="A">
      <item>1</item>
      <item>7</item>
   </item>
   <item type="B">
      <item>2</item>
      <item>4</item>
      <item>5</item>
   </item>
   <item type="C">
      <item>3</item>
      <item>6</item>
   </item>
</items>

This is a traditional way(using preceding-sibling axis, or optionally following-sibling axis):

Code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/items">
        <xsl:copy>
            <xsl:apply-templates select="item[not(preceding-sibling::item/@type = @type)]" mode="group"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="item" mode="group">
        <item type="{@type}">
            <xsl:apply-templates select="../item[@type = current()/@type]" mode="loop"/>
        </item>
    </xsl:template>

    <xsl:template match="item" mode="loop">
        <xsl:copy>
            <xsl:value-of select="."/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>


Explanation:

The first template that matches "/items"(root element) writes the <items>, using <xsl:copy>, the result tree and within this root element applies templates to using the following:

Code:
<xsl:apply-templates select="item[not(preceding-sibling::item/@type = @type)]" mode="group"/>

It tells the processor to apply template rules (with mode="group" and that matches "item") to those "item" elements in the context node which do not have any preceding-sibling "item" elements with the same "@type" as the current "item" element. Thus, applying templates to first "item" elements of each @type.

This makes the processor to apply the second template to all those "item" which match the above mentioned criteria.
(Note: the second template has mode="loop" and it matches item elements).

In the second template, the processor creates an <<item>> element with the @type equal to the current group's @type. And, in this created <<item>> element, applying the third template on every <<item>> having the same @type as the current @type in <<items>>(traversing back to the parent node of the context(which again matches <<item>> and with mode="loop").

And the third loop creates an <<item>> element(using <xsl:copy>) with value of the current context <<item>> element.

Note: Alternatively, we can use "following-sibling" axis instead of "preceding-sibling" axis.
-Lingamurthy CS
Reply

Check for new replies

Forum Jump:


Users browsing this thread: 1 Guest(s)