Re: [xsl] Is this about grouping ?

Subject: Re: [xsl] Is this about grouping ?
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Mon, 2 Jul 2001 17:06:12 +0100
Hi Benoit,

> Of course, I should encapsulate <a> to <i> under <group>.
> But still I'm stuck with my XSLT.

Leaving aside the indentation issue, and assuming that you use group
elements to indicate how the a and b elements apply to particular f to
i elements, you can do the following:

(a) declare the key to access the group elements according to a
particular combination of a and b.  You can use a concatenation of a
and b to give the key value for each group so that you are grouping by
the combination of the two values rather than by just one:

<xsl:key name="groups" match="group" use="concat(a, ':', b)" />

(b) apply templates to the first group with a particular a+b value.
You want to find the group that is the first group returned by the key
with that particular combination of a and b, and apply templates to

<xsl:template match="topic">
      select="group[generate-id() =
                    generate-id(key('groups', concat(a, ':', b))[1])]" />

(c) have a template that matches a group, outputs the values of a and
b for that group and then copies the values of f to i of all the
groups with that particular a+b combination, as retrieved from the
key. You probably want to use a group element to distinguish the
output from this group from any others:

<xsl:template match="group">
    <xsl:copy-of select="a" />
    <xsl:copy-of select="b" />
    <xsl:variable name="groups"
                  select="key('groups', concat(a, ':', b))" />
    <xsl:copy-of select="$groups/*[self::f or self::g or
                                   self::h or self::i]" />

That's the Muenchian solution to the basic grouping problem. You have
a couple of additional twists which alter it a little. First, you
don't want to use a group element in the output to mark off the
groups, but instead indent the elements to varying degrees. I think
this is a dangerous way of indicating structure in an XML document
because whitespace is often ignored (especially if you use MSXML),
which means you lose precious information.

If you really want to use indentation, then you need to use xsl:text
elements to include the significant whitespace. In your sample XSLT,
you've been using *empty* xsl:text elements to mark off whitespace -
this is a technique that only works if you have some non-whitespace
characters alongside the whitespace ones; if you want to include a
number of spaces, you have to put that in the *content* of the
xsl:text, e.g.:

  <xsl:for-each select="$elements">
     <xsl:text>&#xA;      </xsl:text>
     <xsl:copy-of select="." />

(The &#xA; gives a line break.)

The second complication arises if you don't use group elements in your
source XML. That means you have to use some other element to indicate
the groups - the a element is a good bet - and use the
following-sibling axis to access the relevant elements:

<xsl:key name="groups" match="group"
         use="concat(., ':', following-sibling::b[1])" />

<xsl:template match="topic">
      select="a[generate-id() =
                                concat(a, ':',
                                       following-sibling::b[1]))[1])]" />

<xsl:template match="a">
    <xsl:copy-of select="." />
    <xsl:variable name="b" select="following-sibling::b[1]" />
    <xsl:copy-of select="$b" />
    <xsl:variable name="groups"
                  select="key('groups', concat(., ':', $b))" />
    <xsl:copy-of select="$groups/following-sibling::f[1] |
                         $groups/following-sibling::g[1] |
                         $groups/following-sibling::h[1] |
                         $groups/following-sibling::i[1]" />

Or you might find that a different type of grouping method, such as
one that steps through the elements one by one to create the groups,
is easier to handle.



Jeni Tennison

 XSL-List info and archive:

Current Thread