Help with multi-pass XSLT using node-set ()
I need to display certain data in tabular form and prefer to use multi-pass xslt with node-set () so that I can avoid deploying additional tools (like xsltproc). Right now, I can complete the required task in two steps, i.e.
Step 1: Convert XML-1 to XMl-2 using an identity template (xsl: copy, using xsl: element to add 'dev' and 'qa` dynamic elements):
<projectteam>
<member>
<name>John</name>
<role>dev</role>
<hrs>100</hrs>
</member>
<member>
<name>Peter</name>
<role>qa</role>
<hrs>80</hrs>
</member>
</projectteam>
For
<projectteam>
<member>
<name>John</name>
<dev>100</dev>
</member>
<member>
<name>Peter</name>
<qa>80</qa>
</member>
<projectteam>
And then use another XSLT-FO stylesheet to transform XML # 2 to PDF with the layout you want:
name | dev | qa | ----------------- John | 100 | | Peter | | 80 | ----------------- Total | 100 | 80 |
I tried to use node-set()
(incorrectly believe) to combine both steps, but it doesn't work as the result is like this:
name | dev | qa | ----------------- Total | 0 | 0 |
Stylesheet-1: converts XML-1 to XML-2, imports another stylesheet "projDisplay.xsl", uses node -set () to call the imported stylesheet, but no data is displayed?
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:exslt="http://exslt.org/common"
>
<!-- import stylesheet to display XML-2 in tabular form -->
<xsl:import href="projDisplay.xsl"/>
<xsl:template match="/">
<fo:root>
<xsl:apply-templates/>
</fo:root>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:variable name="newXmlData">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:variable>
<!-- ==> This is where my problem is - it goes to the template defined in -->
<!-- projDisplay.xsl (xslt-fo, pretty big one with page layout etc. hence -->
<!-- not included here, but it works on its own though) as I can see the -->
<!-- table header, and an empty totals row, but non of the rows are displayed -->
<xsl:apply-templates
select="exslt:node-set($newXmlData)/projectteam" mode="display"
/>
</xsl:template>
<!-- replace element 'role' with a new element - role name (i.e. dev or qa) -->
<!-- and set its value as 'hrs -->
<xsl:template match="role">
<xsl:element name="{.}"> <xsl:value-of select="../hrs"/> </xsl:element>
</xsl:template>
<!-- eliminate element 'hrs' -->
<xsl:template match="hrs"/>
</xsl:stylesheet>
The commented section in the stylesheet doesn't work for me. Any suggestions on how to fix it?
Thank!
Here's a solution that works. This is based on Tomalak's original solution with some minor modifications (like mode flags, etc.) Again thanks to Tomalak!
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common"
version="1.0">
<xsl:import href="projDisplay.xsl"/>
<xsl:template match="/">
<!-- store intermediate form as RTF -->
<xsl:variable name="newXmlData">
<xsl:apply-templates mode="filter"/>
</xsl:variable>
<!-- Now apply templates (with xslt-fo) defined in projDisplay.xsl with -->
<!-- the root template as <xsl:template match="projectteam" mode="display"> -->
<!-- to the above RTF (i.e. after the original XML has be convertedr) -->
<xsl:apply-templates select="exslt:node-set($newXmlData)/projectteam" mode="display"/>
</xsl:template>
<!-- use identity templates to copy and modify original XML -->
<xsl:template match="@*|node()" mode="filter">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="filter"/>
</xsl:copy>
</xsl:template>
<!-- replace element 'role' with a new element - role name (i.e. dev or qa) -->
<!-- and set its value as 'hrs -->
<xsl:template match="member/role" mode="filter">
<xsl:element name="{.}"> <xsl:value-of select="../hrs"/> </xsl:element>
</xsl:template>
<!-- eliminate element 'hrs' -->
<xsl:template match="hrs" mode="filter"/>
</xsl:stylesheet>
What about:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:exslt="http://exslt.org/common"
exclude-result-prefixes="exslt"
>
<xsl:import href="projDisplay.xsl"/>
<xsl:template match="/">
<!-- store intermediary format as a RTF -->
<xsl:variable name="newXmlData">
<xsl:apply-templates />
</xsl:variable>
<!-- now we can the apply imported rules -->
<xsl:apply-templates
select="exslt:node-set($newXmlData)/projectteam"
mode="import"
/>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="member/role">
<xsl:element name="{.}">
<xsl:value-of select="../hrs" />
</xsl:element>
</xsl:template>
<xsl:template match="member/*" />
<xsl:template match="projectteam" mode="import">
<fo:root>
<xsl:apply-imports />
</fo:root>
</xsl:template>
</xsl:stylesheet>
source to share