Hello xsl-list,
the question of Charlie brought me to thinking about a problem and solution
that I came up with a few years ago.
A few words upfront:
- This is a _very_ stripped down example which doesn't reflect the complete
structure. I hope that I was able to retain all relevant structural
attributes that shape the solution.
- I included <parentA> and <parentB> in the example to account for the fact
that there are at least 20 different parents where the relevant <points>
can occur as childs. It's also possible that a parent holds relevant
<points>, completely different nodes and also another parent.
- I don't want to include <xsl:for-each-group > in every possible parent
for the sake of maintainability. Because of the loads of different child
nodes it seems to me atm that it's not sensible to write specialized
select-statements in apply-templates. Just select="@* | node()" and let the
child-nodes "somehow" take care of themselves.
My solution is somewhat like the following
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" encoding="UTF-8"
media-type="text/xml"/>
<xsl:template match="parentA">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="parentB">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
<special/>
</xsl:template>
<xsl:template match="@* | node()" priority="-1">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="point[@type='1']">
<xsl:message>stripped point</xsl:message>
</xsl:template>
<!-- in the first position or directly after a node() that is not part of
the group -->
<xsl:template match="point[@type='1' and ((position() = 1) or
(preceding-sibling::*[1][self::point[not(@type='1')] or
not(self::point)]))]"
priority="10">
<xsl:variable name="first-point-not-in-group"
select="following-sibling::*[self::point[not(@type='1')] or
not(self::point)][1]"/>
<group>
<!-- apply-templates to self and everything that follows until the
next point that is not part of the group -->
<xsl:apply-templates select=". |
following-sibling::*[generate-id(self::*) ne
generate-id($first-point-not-in-group) and
(every $i in preceding-sibling::point satisfies (generate-id($i) ne
generate-id($first-point-not-in-group)))]" mode="grouping"/>
</group>
</xsl:template>
<xsl:template match="node()" mode="grouping">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I would be interested in ways to tackle this. Are there patterns for this
type of grouping problem? Is it favorable to put "for-each-group" stmts all
over the place? In general: are there better ways to solve this?
The sample input:
<base>
<parentA>
<element />
<point value="A-A" type="1" />
<point value="A-B" type="1" />
<point value="A-C" type="2" />
<point value="A-D" type="1" />
<point value="A-E" type="2" />
<point value="A-F" type="1" />
<point value="A-G" type="1" />
<point value="A-H" type="1" />
</parentA>
<parentB>
<point value="B-A" type="1" />
<point value="B-B" type="1" />
<point value="B-C" type="2" />
<element />
<point value="B-D" type="1" />
<point value="B-E" type="1" />
</parentB>
</base>
The expected output:
<?xml version="1.0" encoding="UTF-8"?>
<base>
<parentA>
<element/>
<group>
<point value="A-A" type="1"/>
<point value="A-B" type="1"/>
</group>
<point value="A-C" type="2"/>
<group>
<point value="A-D" type="1"/>
</group>
<point value="A-E" type="2"/>
<group>
<point value="A-F" type="1"/>
<point value="A-G" type="1"/>
<point value="A-H" type="1"/>
</group>
</parentA>
<parentB>
<group>
<point value="B-A" type="1"/>
<point value="B-B" type="1"/>
</group>
<point value="B-C" type="2"/>
<element/>
<group>
<point value="B-D" type="1"/>
<point value="B-E" type="1"/>
</group>
</parentB>
<special/>
</base>
Thank you in advance for any insight!
Best regards
Christoph Naber
--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com
--~--