Re: Creating different types of output

Subject: Re: Creating different types of output
From: Jeni Tennison <Jeni.Tennison@xxxxxxxxxxxxxxxx>
Date: Tue, 20 Jun 2000 20:02:58 +0100

>I would also like to create annual listings from the same source, 
>and I didn't think it would be all that hard.  However, 
>it seems to require more programming
>skills than I have (which are close-to-none).
>The source file has all the years coded in the XML file. When I run
>my "build year" script, I get one HTML file for each year.  However, I
>only get one record for each year, and that record is always the last
>alphabetical listing for that year.  So what seems to be happening 
>is that each time a new year is encountered, the year output file is
>overwritten, rather than the new record being appended to the
>existing output file.

The problem with the stylesheet that you gave is that it matches on each
individual dpsrecord, and each time it matches, it creates a file for that
particular dpsrecord.  You are right that the output file is being
overwritten because there are several dpsrecords with the same year.
Overwriting is sensible behaviour - it is difficult to append to XML files
because they have to be well-formed, which means it's not as simple as just
adding elements to the end of a file.

I'm not sure from your example whether your file is ordered by year of
death already, but I assume that it's not, that it's ordered by surname
instead, and that the 'dpsletter' elements hold dpsrecords for people whose
surnames start with the same letter.

Your overall problem is a grouping problem as well as an output problem.
You want to group all the 'dpsrecord' elements that have the same
deathdate/year together in the same output file.  When I see a grouping
problem I immediately think of keys, because keys allow you to group
elements together according to some characteristic of the element, in this
case the deathdate.

The first thing I would do is to identify which dpsrecords should be
grouped together, and define a key.  When you define a key, using the
xsl:key element, there are three attributes:
* name: the name of the key, can be anything you like (in your case
'records', say)
* match: the type of nodes that need to be grouped together (in your case
'dpsrecord' elements)
* use: the thing that you group on, as an XPath from a matched node (in
your case the value of deathrate/year)

This gives a key of:

  <xsl:key name="records" match="dpsrecord" use="deathdate/year" />

Defining a key like this enables you to get a list of all the dpsrecords
from a particular year (ordered in the same order as they appear in your
input) by using the 'key' function.  So, for example:

  key('records', '1993')

would give you a node list of all the dpsrecords in which the dead person
died in 1993, in alphabetical order by surname.

You want to create a new document for each of those sets of dpsrecords.
The standard way of doing this consists of two parts:

1. identifying the first dpsrecord in the set
2. creating output for all the dpsrecords in that set

You have to go through this two-stage process because you usually don't
know in advance what the keys are that are used to identify the groups.  In
your case you could do it by stepping through a year at a time, because the
keys you're using are numerical and you could provide information about the
earliest and latest year in your file.  I'm not going to show you that
approach, though, firstly because it's not as general, and secondly because
it would, by design, involve you updating your stylesheet each year, which
isn't good maintenance practice.

Identifying the first dpsrecord in a set involves testing to see whether a
particular dpsrecord that you know about is the same as the first dpsrecord
you get when you retrieve the list of dpsrecords with the same death year.
You should probably use generate-id() to test whether two nodes are
equivalent.  An XPath expression that generates all dpsrecords that are
first in their death-year group is:

  /set/dpsletter/dpsrecord[generate-id() =
                           generate-id(key('records', deathdate/year)[1])]

You should make sure that you are only processing those dpsrecords, by
selecting them when you apply-templates (presumably within the
'set'-matching template) e.g.:

<xsl:template match="set">
    select="dpsletter/dpsrecord[generate-id() =
            generate-id(key('records', deathdate/year)[1])]" />

Now you can guarantee that your dpsrecord-matching template is only matched
once for each death year.  That means that within it you can generate all
the output you want in a particular file, without worrying about it being
overwritten.  This should include iterating over the dpsrecords from that
particular death year, adding their information into the file, using
xsl:for-each.  So:

<xsl:template match="dpsrecord">
  <xsl:variable name="filename">
    <xsl:value-of select="deathdate/year"/>
  <xt:document href="{concat($filename,'.shtml')}">
        <title>Dead People Server - People Who Died in the Year
<xsl:value-of select="deathdate/year"/></title>
        <LINK REL="stylesheet" HREF="dps.css" TYPE="text/css" />
        <xsl:for-each select="key('records', $filename)">
            <xsl:apply-templates />

A couple of comments: I've used $filename rather than deathdate/year in the
key() function because you've already retrieved that information, so you
may as well reuse it.  Also, if you want to have another
'dpsrecord'-matching template that produces the information for that
dpsrecord, rather than generating a page, then you could use modes to
distinguish between them.

This has been tested and works in SAXON, albeit using the extension
function with the equivalent functionality to xt:document, and not having a
full set of dpsrecords to play with.

I hope this helps,


Dr Jeni Tennison
Epistemics Ltd, Strelley Hall, Nottingham, NG8 6PE
Telephone 0115 9061301 ? Fax 0115 9061304 ? Email

 XSL-List info and archive:

Current Thread