On Mon, 19 Jun 2006 12:56:42 +0200, Chris <c.******@swansea.ac.uk> wrote:
I have a problem with grouping elements in my XSL. What I want to do
is select all of the distinct SCE_CGPC records, then by the FSG_SNAM
records for each SCE_CGPC and then again by MOA_NAME for each SCE_CGPC
and FSG_SNAM.
I have included a achunk of my XML to help expalin this:
This problem is easily solved with concat-augmented muenchian grouping
(
http://www.dpawson.co.uk/xsl/sect2/N...tml#d5171e685).
However, a generic solution to group nodes to an arbitrary level in XSLT
1.0 is quite tempting.
Here's my attempt:
==input==
<xml xmlns:z="z" xmlns:rs="rs">
<rs:data>
<z:row SCE_CGPC='PGRE' FSG_SNAM='HOME' MOA_NAME='Full time'/>
<z:row SCE_CGPC='PGRE' FSG_SNAM='OVERSEAS' MOA_NAME='Full time'/>
<z:row SCE_CGPC='PGRE' FSG_SNAM='HOME MOA_NAME' MOA_NAME='Full time'/>
<z:row SCE_CGPC='PGRE' FSG_SNAM='HOME' MOA_NAME='Part-Time'/>
<z:row SCE_CGPC='PGRE' FSG_SNAM='OVERSEAS' MOA_NAME='Full time'/>
</rs:data>
</xml>
==XSLT==
<?xml version='1.0' encoding='utf-8' ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" xmlns:z="z" xmlns:rs="rs">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="row" match="z:row" use="@SCE_CGPC"/>
<xsl:key name="row" match="z:row" use="@FSG_SNAM"/>
<xsl:key name="row" match="z:row" use="@MOA_NAME"/>
<xsl:variable name="keys"
select="document('')/xsl:stylesheet/xsl:key[@name='row']/@use"/>
<xsl:template match="rs:data">
<grouped>
<xsl:call-template name="group"/>
</grouped>
</xsl:template>
<xsl:template match="z:row">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template name="group">
<xsl:param name="level" select="1"/>
<xsl:param name="nodes" select="/xml/rs:data/z:row"/>
<xsl:param name="groupkey" select="$keys[$level]"/>
<xsl:param name="groupkeys" select="$keys[position() <= $level ]"/>
<xsl:for-each
select="/xml/rs:data/z:row[generate-id(.)=generate-id(key('row',@*[concat('@',name())=$groupkey]))]">
<group foo="{count($nodes)}" level="{$level}"
groupcode="{concat($groupkey,'=',@*[concat('@',name())=$groupkey])}">
<xsl:for-each
select="$nodes[@*[concat('@',name())=$groupkey]=current()/@*[concat('@',name())=$groupkey]][1]">
<xsl:variable name="all"
select="key('row',@*[concat('@',name())=$groupkeys])"/>
<xsl:variable name="set"
select="key('row',@*[concat('@',name())=$groupkeys])|dummy"/>
<xsl:variable name="filter">
<xsl:for-each select="$set">
<xsl:variable name="numoccur"
select="count($all[generate-id(.)=generate-id(current())])"/>
<xsl:if test="$numoccur=$level">+</xsl:if>
<xsl:if test="not($numoccur=$level)">-</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="pre_filtered"
select="$set[substring($filter,position(),1)='+']"/>
<xsl:variable name="filtered"
select="$nodes[count(.|$pre_filtered)=count($pre_filtered)]"/>
<xsl:if test="$level = 3">
<xsl:apply-templates select="$filtered"/>
</xsl:if>
<xsl:if test="$level != 3">
<xsl:call-template name="group">
<xsl:with-param name="level" select="$level + 1" />
<xsl:with-param name="nodes" select="$filtered" />
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</group>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
==output==
<?xml version="1.0" encoding="UTF-8"?>
<grouped xmlns:rs="rs" xmlns:z="z">
<group foo="5" level="1" groupcode="@SCE_CGPC=PGRE">
<group foo="5" level="2" groupcode="@FSG_SNAM=HOME">
<group foo="2" level="3" groupcode="@MOA_NAME=Full time">
<z:row SCE_CGPC="PGRE" FSG_SNAM="HOME" MOA_NAME="Full time"/>
</group>
<group foo="2" level="3" groupcode="@MOA_NAME=Part-Time">
<z:row SCE_CGPC="PGRE" FSG_SNAM="HOME" MOA_NAME="Part-Time"/>
</group>
</group>
<group foo="5" level="2" groupcode="@FSG_SNAM=OVERSEAS">
<group foo="2" level="3" groupcode="@MOA_NAME=Full time">
<z:row SCE_CGPC="PGRE" FSG_SNAM="OVERSEAS" MOA_NAME="Full time"/>
<z:row SCE_CGPC="PGRE" FSG_SNAM="OVERSEAS" MOA_NAME="Full time"/>
</group>
<group foo="2" level="3" groupcode="@MOA_NAME=Part-Time"/>
</group>
<group foo="5" level="2" groupcode="@FSG_SNAM=HOME MOA_NAME">
<group foo="1" level="3" groupcode="@MOA_NAME=Full time">
<z:row SCE_CGPC="PGRE" FSG_SNAM="HOME MOA_NAME" MOA_NAME="Full time"/>
</group>
<group foo="1" level="3" groupcode="@MOA_NAME=Part-Time"/>
</group>
</group>
</grouped>
This code relies on the observation that the xslt 'key' function does not
remove duplicates in the node-set it returns.
It is possible that this observation is a result of an erroneous
implementation of my xslt-processor. This would of course render the
solution useless...
regards,
--
Joris Gillis (
http://users.telenet.be/root-jg/me.html)
«Εν οίδα ότι ουδ*ν οίδα» - Σωκρατης