Main Template for Advanced xsl:for-each Example

Since this XSLT stylesheet produces tables with essentially the same structure as the simple <xsl:for-each> example, the main template is similar to the one used in the simple example.

Figure 1. Main template of customized_frequency_tables.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0" xmlns:oms="http://xml.spss.com/spss/oms">
  
<!--enclose everything in a template, starting at the root node-->
<xsl:template match="/">
<HTML>
<HEAD>
<TITLE>Modified Frequency Tables</TITLE>
</HEAD>
<BODY>
<xsl:for-each select="//oms:pivotTable[@subType='Frequencies']">
<xsl:for-each select="oms:dimension[@axis='row']">
 <h3>
  <xsl:call-template name="showVarInfo"/>
 </h3>
</xsl:for-each>
<!--create the HTML table-->
<table border="1">
 <tbody align="char" char="." charoff="1">
 <tr>  <th>Category</th><th>Count</th><th>Percent</th>
 </tr>
 <xsl:for-each select="descendant::oms:dimension[@axis='column']">
   <xsl:if test="oms:category[3]">
    <tr>
     <td>
       <xsl:choose>
        <xsl:when test="parent::*/@varName">
         <xsl:call-template name="showValueInfo"/>
        </xsl:when>
        <xsl:when test="not(parent::*/@varName)">
          <b><xsl:value-of select="parent::*/@text"/></b>
        </xsl:when>
       </xsl:choose>
     </td>
     <td>
      <xsl:apply-templates select="oms:category[1]/oms:cell/@number"/>
     </td>
     <td>
      <xsl:apply-templates select="oms:category[3]/oms:cell/@number"/>
     </td>
    </tr>
  </xsl:if>
  </xsl:for-each>
 </tbody>
</table>
<xsl:if test="descendant::*/oms:note">
<p><xsl:value-of select="descendant::*/oms:note/@text"/></p>
</xsl:if>
</xsl:for-each>
</BODY>
</HTML>
</xsl:template>

This template is very similar to the one for the simple example. The main differences are:

  • <xsl:call-template name="showVarInfo"/> calls another template to determine what to show for the table title instead of simply using the text attribute of the row dimension (oms:dimension[@axis='row']). See the topic Controlling Variable and Value Label Display for more information.
  • <xsl:if test="oms:category[3]"> selects only the data in the 'Valid' section of the table instead of <xsl:if test="ancestor::oms:group[@text='Valid']">. The positional argument used in this example doesn't rely on localized text. It relies on the fact that the basic structure of a frequency table is always the same and the fact that OXML does not include elements for empty cells. Since the 'Missing' section of a frequency table contains values only in the first two columns, there are no oms:category[3] column elements in the 'Missing' section, so the test condition is not met for the 'Missing' rows. See the topic XPath Expressions in Multiple Language Environments for more information.
  • <xsl:when test="parent::*/@varName"> selects the nontotal rows instead of <xsl:when test="not((parent::*)[@text='Total'])">. Column elements in the nontotal rows in a frequency table contain a varName attribute that identifies the variable, whereas column elements in total rows do not. So this selects nontotal rows without relying on localized text.
  • <xsl:call-template name="showValueInfo"/> calls another template to determine what to show for the row labels instead of <xsl:value-of select="parent::*/@text"/>. See the topic Controlling Variable and Value Label Display for more information.
  • <xsl:apply-templates select="oms:category[1]/oms:cell/@number"/> selects the value in the 'Frequency' column instead of <xsl:value-of select="oms:category[@text='Frequency']/oms:cell/@text"/>. A positional argument is used instead of localized text (the 'Frequency' column is always the first column in a frequency table), and a template is applied to determine how to display the value in the cell. Percentage values are handled the same way, using oms:category[3] to select the values from the 'Valid Percent' column. See the topic Controlling Decimal Display for more information.