tags:

views:

632

answers:

2

I need to display certain data in a tabular form, and would prefer to use multi-pass xslt using node-set() so that I can avoid deploying additional tools (like xsltproc). Right now, I'm able to perform the required task in two steps i.e.

Step 1: convert XML-1 to XMl-2 using identity template (xsl:copy, using xsl:element to add dynamic elements 'dev' and 'qa`):

<projectteam>
  <member>
    <name>John</name>
    <role>dev</role>
    <hrs>100</hrs>
  </member>
  <member>
    <name>Peter</name>
    <role>qa</role>
    <hrs>80</hrs>
  </member>
</projectteam>

To

<projectteam>
  <member>
    <name>John</name>
    <dev>100</dev>
  </member>
  <member>
    <name>Peter</name>
    <qa>80</qa>
  </member>
<projectteam>

And then, use another XSLT-FO style sheet to transform XML #2 into a PDF document with the required layout:

name | dev | qa |
-----------------
John | 100 |    |
Peter|     | 80 |
-----------------
Total| 100 | 80 |

I've tried using node-set() (incorrectly I suppose) to combine both the steps, but it wouldn't work as the result I get is as follows:

name | dev | qa |
-----------------
Total|  0  |  0 |

Stylesheet-1: converts XML-1 to XML-2, imports another stylesheet 'projDisplay.xsl', uses node-set() to invoke the imported stylesheet, but the data doesn't get 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 look right to me. Any suggestions about how to correct it?

Thanks!

A: 

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>
Tomalak
No, that didn't work. btw, xsl:apply-imports doesn't accept select and mode flags. Then, I tried replacing apply-imports with apply-templates, and it didn't work either. FOP complains about unknownformatting object for each of the elements in the stylesheet. But, it does complaint about 'dev' and 'qa' along with rest of the elements, so the translation has happened...So, what's wrong here?
Krishna
No, doesn't work either; I get this error: xsl:apply-imports not allowed in a xsl:for-each
Krishna
+1  A: 

Here's the solution that works. It's 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>
Krishna