Reputation: 13
i have a XML datadump from a database that i want to present using xslt. The structure of the data is not 'straightforward' for the layout that i want (i also use the XML data for another report).
What i want is to do some calculations on data from Level-A where i need to group on Level-C children.
I know i can probably select the data again into an XML file where the structure is 'easy' for my report, but that is my last resort because i have the feeling that it can also be accomplished in XSLT itself. Most probbaly i need some 'Muenchian' trick to get it done, but since i am a 'Muenchian Virgin' i get stuck in every attempt (that i try to 'steal' and change ...).
Does anybody know if Muenchian is the way to proceed and can somebody help me to get on the right track ? I did some reading (including Jeni Tennison's), but the stuff i have seen so far is not covering my problem as far as i know ...
Below is a simplified XML structure that is (more or less) representative for my real problem.
Any ideas?
Kind regards, Henk
<data>
<a>
<a_id>A1</a_id>
<a_desc>A one</a_desc>
<a_val>1</a_val>
<b>
<c>
<c_id>C2</c_id>
<c_desc>C two</c_desc>
</c>
</b>
</a>
<a>
<a_id>A2</a_id>
<a_desc>A two</a_desc>
<a_val>2</a_val>
<b>
<c>
<c_id>C2</c_id>
<c_desc>C two</c_desc>
</c>
</b>
</a>
<a>
<a_id>A3</a_id>
<a_desc>A three</a_desc>
<a_val>3</a_val>
<b>
<c>
<c_id>C1</c_id>
<c_desc>C one</c_desc>
</c>
</b>
</a>
<a>
<a_id>A4</a_id>
<a_desc>A four</a_desc>
<a_val>7</a_val>
<b>
<c>
<c_id>C3</c_id>
<c_desc>C three</c_desc>
</c>
</b>
</a>
<a>
<a_id>A5</a_id>
<a_desc>A five</a_desc>
<a_val>11</a_val>
<b>
<c>
<c_id>C1</c_id>
<c_desc>C one</c_desc>
</c>
</b>
</a>
</data>
C_desc Count() Sum(a_val) Avg(a_val)
------ ------- ---------- ----------
C one 3 15 5
C two 1 2 2
C three 1 7 7
Upvotes: 1
Views: 116
Reputation: 70648
As you mention, Muenchian grouping is the way to go (in XSLT1.0). You say you want to group a elements, using values in a c element. Therefore you would define a key like so:
<xsl:key name="a" match="a" use="b/c/c_desc" />
Then, you need to get 'distinct' a elements, which is done by selecting a elements that happen to be the first element in the group for a given key. You do this with this fairly scary expression
<xsl:apply-templates
select="//a[generate-id() = generate-id(key('a', b/c/c_desc)[1])]" />
Here, key('a', b/c/c_desc)[1]
will find the first element in the key's group, and then you use generate-id
to compare the elements.
Then, you would have a template to match the distinct a elements, and within in this you can then do calculations on the group. For example, to get the sum:
<xsl:value-of select="sum(key('a', b/c/c_desc)/a_val)" />
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="exsl">
<xsl:output method="html" indent="yes"/>
<xsl:key name="a" match="a" use="b/c/c_desc" />
<xsl:template match="/">
<table>
<tr>
<td>C_desc</td>
<td>Count</td>
<td>Sum</td>
<td>Avg</td>
</tr>
<xsl:apply-templates select="//a[generate-id() = generate-id(key('a', b/c/c_desc)[1])]" />
</table>
</xsl:template>
<xsl:template match="a">
<xsl:variable name="c_desc" select="b/c/c_desc" />
<tr>
<td><xsl:value-of select="count(key('a', $c_desc))" /></td>
<td><xsl:value-of select="sum(key('a', $c_desc)/a_val)" /></td>
<td><xsl:value-of select="sum(key('a', $c_desc)/a_val) div count(key('a', $c_desc))" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output
<table>
<tr>
<td>C_desc</td>
<td>Count</td>
<td>Sum</td>
<td>Avg</td>
</tr>
<tr>
<td>3</td>
<td>15</td>
<td>5</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>1</td>
<td>7</td>
<td>7</td>
</tr>
</table>
Upvotes: 1