I've a need to display certain XML content in tabular form (XSL-FO for pdf reports),
and not all the columns to displayed are present in source XML. So, I was wondering
if there is a way to transform source XML by embedding additional columns based on
certain element values, and then process the resulting XML to display content?
As an example, for source data:
<projectteam> <member> <name>John Doe</name> <role>dev</role> <hrs>100</hrs> </member> <member> <name>Paul Coder</name> <role>dev</role> <hrs>40</hrs> </member> <member> <name>Henry Tester</name> <role>qa</role> <hrs>80</hrs> </member> <member> <name>Peter Tester</name> <role>qa</role> <hrs>40</hrs> </member> </projectteam>
I'd like the data to be displayed as :
Name Role Dev QA --------------------------- John Doe dev 100 Paul Coder dev 40 Henry Tester qa 80 Peter Tester qa 40 --------------------------- Role Totals: 140 120 ---------------------------
I would like to know if I can use something like:
<xsl:element name="{role}"> <xsl:value-of select="member/hrs"/> </xsl:element>
So that I can embed elements <dev>100</dev> and so on at run time during
first pass, and then use the resulting XML to display data for new columsn 'dev'
and 'qa', that way, calculating totals for each role type will be much simpler
(for eg. "sum(preceding-sibling::member/dev)" for dev column), and the data for each
cell in 'dev' and 'qa' colums could simply be the value-of these tags respectively.
It got the desired results the hard way using following stylesheet (page formatting
details omitted to keep it brief), but am not convinced that this is the apt solution.
... <fo:table-body> <!-- fills table rows --> <xsl:apply-templates select="member"/> <!-- dislpay totals for each role --> <fo:table-row height="12pt" border-bottom="1pt solid black"> <fo:table-cell number-columns-spanned="2"> <fo:block>Role Totals:</fo:block> </fo:table-cell> <fo:table-cell text-align="right"> <xsl:call-template name="RoleTotals"> <xsl:with-param name="node" select="//member[1]"/> <xsl:with-param name="roleName" select="'dev'"/> </xsl:call-template> </fo:table-cell> <fo:table-cell text-align="right"> <xsl:call-template name="RoleTotals"> <xsl:with-param name="node" select="//member[1]"/> <xsl:with-param name="roleName" select="'qa'"/> </xsl:call-template> </fo:table-cell> </fo:table-row> </fo:table-body> ... </fo:root> </xsl:template> <xsl:template match="member"> <fo:table-row border-bottom="1pt solid black"> <fo:table-cell> <fo:block> <xsl:value-of select="name"/></fo:block></fo:table-cell> <fo:table-cell> <fo:block> <xsl:value-of select="role"/></fo:block></fo:table-cell> <fo:table-cell text-align="right"> <fo:block> <xsl:if test="role = 'dev'"><xsl:value-of select="hrs"/></xsl:if> </fo:block> </fo:table-cell> <fo:table-cell text-align="right"> <fo:block> <xsl:if test="role = 'qa'"><xsl:value-of select="hrs"/></xsl:if> </fo:block> </fo:table-cell> </fo:table-row> </xsl:template> <xsl:template name="RoleTotals"> <xsl:param name="node"/> <xsl:param name="roleName"/> <xsl:param name="RT" select="0"/> <xsl:variable name="newRT"> <xsl:choose> <xsl:when test="$node/role = $roleName"> <xsl:value-of select="$RT + $node/hrs"/> </xsl:when> <xsl:otherwise><xsl:value-of select="$RT"/></xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:choose> <xsl:when test="$node/following-sibling::member"> <xsl:call-template name="RoleTotals"> <xsl:with-param name="node" select="$node/following-sibling::member[1]"/> <xsl:with-param name="roleName" select="$roleName"/> <xsl:with-param name="RT" select="$newRT"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <fo:block><xsl:value-of select="$newRT"/></fo:block> </xsl:otherwise> </xsl:choose> </xsl:template>
Best Answer
What would happen if more job roles, other than Dev and QA got added? Would your stylesheet be able to cope? Maybe you can make use of Muenchian Grouping to get all possible roles in the stylesheet, and then generate columns for each possible role dynamically?
I've outputted as HTML, not XSL-FO, but maybe this gives you the general idea.