Question

Newbie question. I have to create a cross tab report like this:

                       SC     TIN
Total                   3       2
  South East            2       0
    Gold Coast          2       0
  North                 1       2
    Cairns              0       1
    Townsville          1       1 

using the (simplified) XML below:

<DATASETLIST>
 <DATASET entity="SC">
  <ROW>
   <District>GOLD COAST</District> 
   <Region>SOUTH EAST</Region> 
  </ROW>
  <ROW>
   <District>GOLD COAST</District> 
   <Region>SOUTH EASTERN</Region> 
  </ROW>
  <ROW>
   <District>TOWNSVILLE</District> 
   <Region>NORTH</Region> 
  </ROW>
 </DATASET>
 <DATASET entity="TIN">
  <ROW>
   <District>TOWNSVILLE</District> 
   <Region>NORTH</Region> 
  </ROW>
  <ROW>
   <District>CAIRNS</District> 
   <Region>NORTH</Region> 
  </ROW>
 </DATASET>
</DATASETLIST>

I've actually got the main report working however the hierarchy is hardcoded (e.g. count SC's linked to "Gold Coast") but because of possible poor data, I could miss some nodes so I want to build a unique hierarchy based on the data, and then I can call my "count" template for each node.

I've got to a point (see below) where I can produce a region-sorted list but it's not unique and the districts are not sorted within each region. I was thinking that I could somehow create a unique hierarchy variable and then process that with xsl:for-each logic.

But I have no idea whether this is possible or the recommended method. It's possibly an extremely simple solution, but I have no idea how to go about it. Please any answers in simple (and clearly explained) terms thanks.

<xsl:for-each select="//Region[not(Region=preceding-sibling::Region)]">
    <xsl:sort select="."/>
    <xsl:for-each select=".">
        <xsl:value-of select="."/> - 
        <xsl:for-each select="../District"> 
            <xsl:value-of select="."/><br/>
        </xsl:for-each>
    </xsl:for-each>
</xsl:for-each>
Was it helpful?

Solution

This is how you can do it. It is not easy and straightforward I'm afraid, though. The way your input XMl is structured makes it harder to access the relevant nodes.

For example, in several places you need to access nodes that are naturally not in the context. To adress this I use variables and parameters.

But it is quite generic, i.e. you can process an arbitrary number of DATASETS, Regions or Districts. It creates an HTML table to display the counts.

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">
  <xsl:element name="html">
     <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

<xsl:template match="DATASETLIST">
  <xsl:element name="head">
     <xsl:element name="title">
        <xsl:text>DATASET counts</xsl:text>
     </xsl:element>
  </xsl:element>
  <xsl:element name="body">
     <xsl:element name="table">
        <xsl:attribute name="border">1</xsl:attribute>
        <xsl:call-template name="tbl"/>
     </xsl:element>
  </xsl:element>
</xsl:template>

<xsl:template name="tbl">
  <xsl:param name="regions" select="distinct-values(DATASET/ROW/Region)"/>
  <xsl:param name="rows" select="DATASET/ROW"/>
  <xsl:param name="datasets" select="DATASET"/>

  <!--header row-->
  <xsl:element name="tr">
     <xsl:element name="th"/>
     <xsl:for-each select="$datasets">
        <xsl:element name="th">
           <xsl:value-of select="@entity"/>
        </xsl:element>
     </xsl:for-each>
  </xsl:element>
  <!--Totals row-->
  <xsl:element name="tr">
     <xsl:element name="td">
        <xsl:element name="b">
           <xsl:text>Total</xsl:text>
        </xsl:element>
     </xsl:element>
     <xsl:for-each select="$datasets">
        <xsl:element name="td">
           <xsl:value-of select="count(ROW)"/>
        </xsl:element>
     </xsl:for-each>
  </xsl:element>
  <xsl:element name="tr"/>
  <!--region rows-->
  <xsl:for-each select="$regions"> 
     <xsl:variable name="region" select="."/>       
     <xsl:element name="tr">
        <xsl:element name="td">
           <xsl:element name="b">
              <xsl:value-of select="."/>
           </xsl:element>
        </xsl:element>
        <xsl:for-each select="$datasets">
           <xsl:element name="td">
              <xsl:value-of select="count(ROW/Region[.=$region])"/>
           </xsl:element>
        </xsl:for-each>
     </xsl:element>
     <!--district rows-->
     <xsl:for-each select="distinct-values($rows[Region=$region]/District)">
        <xsl:variable name="district" select="."/>
        <xsl:element name="tr">
           <xsl:element name="td">
              <xsl:value-of select="."/>
           </xsl:element>
           <xsl:for-each select="$datasets">
              <xsl:element name="td">
                 <xsl:value-of select="count(ROW/District[.=$district])"/>
              </xsl:element>
           </xsl:for-each>
        </xsl:element>
     </xsl:for-each>
  <xsl:element name="tr"/>
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top