RE: [xsl] xslt: Wrapping multiple elements in a parent element

Subject: RE: [xsl] xslt: Wrapping multiple elements in a parent element
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Fri, 09 May 2003 12:48:05 -0400
Hi Steve,

As for your question, it was a bit unclear (especially as to the structure of your source), but it sounds like a variant of a grouping problem. Hint: try looking at keys to associate elements that you want to group with another "key" element; when that element is processed, a wrapper can be created and the group processed.

I dunno if Sundar's code will do it for you (I hope so!); in any case, I thought his solution was worth looking at as an exercise:

<?xml version="1.0"?>
<Root>
        <a>
                <b>
                        <c>
                        <d>sundar</d>
                        <e>
                                <f>fff</f>
                        </e>
                        </c>
                        <d>sundar2</d>
                </b>
        </a>
        <a>
                <b>
                        <d>sundar3</d>
                </b>
        </a>
        <d>sundar4</d>
</Root>

I am trying to group all the <d> elements separately and rest of the
document will be same.
<d> elements will be grouped under <D> element.

This is the stylesheet which does that:

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

<xsl:template match="/Root">
<Root>
        <xsl:apply-templates select="*[name(.)!='d']"/>
        <D>
        <xsl:apply-templates select="//d"/>
        </D>
</Root>
</xsl:template>

<xsl:template match="*">
        <xsl:element name="{name(.)}">
                <xsl:value-of select="text()"/>
        <xsl:apply-templates select="*[name(.)!='d']"/>
        </xsl:element>
</xsl:template>

<xsl:template match="d">
        <d>
                <xsl:value-of select="text()"/>
        </d>
</xsl:template>

</xsl:stylesheet>

An alternative (accomplishes exactly the same thing):


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

<xsl:template match="/Root">
  <Root>
    <xsl:apply-templates/>
    <D>
      <xsl:apply-templates mode="dMode"/>
    </D>
  </Root>
</xsl:template>

<xsl:template match="*">
  <!-- copies elements in default traversal -->
  <xsl:copy>
    <xsl:apply-templates/>
  </xsl:copy>
</xsl:template>

<xsl:template match="d"/>
<!-- suppresses d element in default traversal -->

<xsl:template match="d" mode="dMode">
  <!-- in dMode, we copy the d -->
  <xsl:copy-of select=".">
</xsl:template>

<xsl:template match="text()" mode="dMode"/>
<!-- when in dMode, we want to suppress everything else;
     this accomplishes that by suppressing the text nodes -->

</xsl:stylesheet>

The differences between the "pull" (Sundar's) and this "push" stylesheet are instructive. Although they create the same output, there are differences for maintenance and extensibility. Many will prefer the "pull" approach since it doesn't rely on the built-in default templates, so is easier to read and understand. (For newbies, let's add them here, at least as they apply to this stylesheet:

<xsl:template match="*">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="text()">
  <xsl:value-of select="."/>
</xsl:template>

<xsl:template match="*" mode="dMode">
  <xsl:apply-templates mode="dMode"/>
</xsl:template>

If you don't know those templates are working, my stylesheet becomes pretty mysterious!)

On the other hand, the "push" approach I used has certain attractions. Where the pull approach works by deliberately "stepping around" the <d> elements where they would be selected, this lets them get matched to templates which happen to do nothing with them, then lets a different tree traversal (in a different mode) go through and copy them into the new wrapper. This general pattern may scale better to complex transformation requirements (you might do this to create endnotes to a publication, for example), though for a one-off it will hardly matter.

One functional difference between the stylesheets is that if you ever had <d> elements wrapped inside other <d> elements, my stylesheet would preserve this wrapping in their copies, where Sundar's would split them out. Probably an academic point (and mine could be easily tweaked to split them out).

Which one you "should" prefer depends totally on how this stylesheet is being used and maintained.

Cheers (and thanks to Sundar) --
Wendell


___&&__&_&___&_&__&&&__&_&__&__&&____&&_&___&__&_&&_____&__&__&&_____&_&&_ "Thus I make my own use of the telegraph, without consulting the directors, like the sparrows, which I perceive use it extensively for a perch." -- Thoreau


XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list



Current Thread