Subject: [xsl] Re: Recursive grouping won't recurse... From: Dimitre Novatchev <dnovatchev@xxxxxxxxx> Date: Thu, 4 Jul 2002 13:21:58 -0700 (PDT) |
--- "Hunsberger, Peter" <Peter dot Hunsberger at stjude dot org> wrote: > I'm still shaky on grouping with keys so I've probably missed > something > obvious, but I can't get a grouping to work when it has to group on a > recursive structure. The input looks essentially like the following: > > <list> > <a type="1" flag="false"/> > <b type="2" flag="false"/> > <c type="3" flag="false"> > <d type="4" flag="true"/> > <e type="4" flag="true"/> > <f type="5" flag="true"/> > <g type="5" flag="true"/> > </c> > </list> > <list> > <a type="1" flag="false"/> > <b type="2" flag="false"/> > <c type="3" flag="false"> > <d type="7" flag="false"> > <e type="4" flag="true"/> > <e type="4" flag="true"/> > </d> > </c> > </list> > > Where, if there are adjacent nodes with the same type than the flag > will be > true, otherwise the flag will always be false. It could be possible > for > adjacent nodes to have the same type with the flag set to false. The > same > structure could go many more levels deep than shown here. The desired > output > is > > <list> > <a type="1" flag="false"/> > <b type="2" flag="false"/> > <c type="3" flag="false"> > <group> > <d type="4" flag="true"/> > <e type="4" flag="true"/> > </group> > <group> > <f type="5" flag="true"/> > <g type="5" flag="true"/> > </group> > </c> > </list> > <list> > <a type="1" flag="false"/> > <b type="2" flag="false"/> > <c type="3" flag="false"> > <d type="7" flag="false"> > <group> > <e type="4" flag="true"/> > <e type="4" flag="true"/> > </group> > </d> > </c> > </list> > > Where any adjacent nodes of the same type and with "flag" = true are > enclosed in a group. The XSLT I have resembles the following > (extracted > from a larger XSLT with other things going on): > > <xsl:key select="." name="menus" match="*" use="@type"/> > > <xsl:template match="list"> > <xsl:apply-templates select="." mode="menu"/> > </xsl:template> > > <xsl:template match="*" mode="menu"> > <xsl:for-each select="*[generate-id() = generate-id(key('menus', > @type))]"> > <xsl:choose> > <xsl:when test="@flag='true'"> > <group> > <xsl:copy select="."> > <xsl:apply-templates select="@* | text()"/> > <xsl:apply-templates select="." mode="menu"/> > </xsl:copy> > <xsl:for-each > select="current()/following-sibling::*[current()/@type = @type]"> > <xsl:copy select="current()"> > <xsl:apply-templates select="@* | text()"/> > <xsl:apply-templates select="." mode="menu"/> > </xsl:copy> > </xsl:for-each> > </group> > </xsl:when> > <xsl:otherwise> > <xsl:copy select="."> > <xsl:apply-templates select="@* | text()"/> > <xsl:apply-templates select="." mode="menu"/> > </xsl:copy> > <xsl:for-each > select="current()/following-sibling::*[current()/@type = @type]"> > <xsl:copy select="current()"> > <xsl:apply-templates select="@* | text()"/> > <xsl:apply-templates select="." mode="menu"/> > </xsl:copy> > </xsl:for-each> > </xsl:otherwise> > </xsl:choose> > </xsl:for-each> > </xsl:template> > > This will produce the desired results for a first level group (as in > the > first list), but does not produce the desired results for a second > level > group (as in the second list). The issue is, I'm sure, with the way > the key > works, but I'm not clear what the problem is? > > I've also tried doing the same thing with using named templates > instead > of > modes with the same results. I'll have to add additional case > handling > templates once I've got this working so I'd rather stick with modes > if > possible... > > BTW, is there a nice way to say "self and siblings" so that you don't > have > to copy the current node and then copy the siblings? > Hi Peter, This is a positional grouping problem. Here's one possible solution: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/> <xsl:key name="kGrp1" match="a|b|c|d|e|f|g" use="number( @flag = 'true' and not(@type = preceding-sibling::*[1]/@type) and following-sibling::*[1]/@flag = 'true' and @type = following-sibling::*[1]/@type )"/> <xsl:strip-space elements="*"/> <xsl:template match="/ | @* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template match="a|b|c|d|e|f|g"> <xsl:choose> <xsl:when test="count(. | key('kGrp1', '1')) = count(key('kGrp1', '1'))"> <group> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> <xsl:variable name="vOutOfGroupSibling" select="following-sibling::*[not(@type = current()/@type and @flag = 'true' ) ][1]"/> <xsl:variable name="vGroupLength"> <xsl:choose> <xsl:when test="$vOutOfGroupSibling"> <xsl:value-of select="count($vOutOfGroupSibling/preceding-sibling::*) - count(preceding-sibling::*)"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="count(../*) - count(preceding-sibling::*)"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:apply-templates mode="inGroup" select="following-sibling::* [position() < $vGroupLength]"/> </group> </xsl:when> <xsl:when test="not(@type = preceding-sibling::*[1]/@type and @flag = 'true' and preceding-sibling::*[1]/@flag = 'true' )"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:when> </xsl:choose> </xsl:template> <xsl:template match="a|b|c|d|e|f|g" mode="inGroup"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> This transformation works correctly on your original xml file. It also works with nested groupings, e.g. when applied on the following source xml: <lists> <list> <a type="1" flag="false"/> <b type="2" flag="false"/> <c type="3" flag="true"> <d type="4" flag="true"/> <e type="4" flag="true"/> <f type="5" flag="true"/> <g type="5" flag="true"/> </c> <c type="3" flag="true"/> </list> <list> <a type="1" flag="false"/> <b type="2" flag="false"/> <c type="3" flag="false"> <d type="7" flag="false"> <e type="4" flag="true"/> <e type="4" flag="true"/> </d> </c> </list> </lists> the result correctly contains nested groups: <lists> <list> <a type="1" flag="false"/> <b type="2" flag="false"/> <group> <c type="3" flag="true"> <group> <d type="4" flag="true"/> <e type="4" flag="true"/> </group> <group> <f type="5" flag="true"/> <g type="5" flag="true"/> </group> </c> <c type="3" flag="true"/> </group> </list> <list> <a type="1" flag="false"/> <b type="2" flag="false"/> <c type="3" flag="false"> <d type="7" flag="false"> <group> <e type="4" flag="true"/> <e type="4" flag="true"/> </group> </d> </c> </list> </lists> Hope this helped. Cheers, Dimitre Novatchev. __________________________________________________ Do You Yahoo!? Sign up for SBC Yahoo! Dial - First Month Free http://sbc.yahoo.com XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
[xsl] creating a key() on a field w, Danny Vint | Thread | [xsl] Mozilla & colspan, Joel Konkle-Parker |
[xsl] creating a key() on a field w, Danny Vint | Date | [xsl] Mozilla & colspan, Joel Konkle-Parker |
Month |