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!

+2


source to share


2 answers


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>

      

+3


source


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>

      

+2


source







All Articles