XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition (726 page)

BOOK: XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition
6.74Mb size Format: txt, pdf, ePub

This shows all the details of one individual, with links to related individuals so that you can browse around the family tree. Of course one could attempt many more ambitious ways of displaying this data, and I would encourage you to do so: you can start with the small Kennedy data set included in the download for this book, and then continue with any other GEDCOM data set, perhaps one of your own family tree.

Because we will have one HTML page for each individual in the file, we have to think about how to create multiple HTML pages from a single XML input document. There are at least three ways of doing this:

  • A bulk publishing process, in which you convert the XML input document into a set of HTML pages, and then publish these as static pages on the Web server. This has the benefit that you only incur the cost of transformation once. It minimizes your dependence on the facilities available from your Internet service provider, and it will work with any browser. However, it can take a lot of space on the server, and it can take a long time to upload if you have a slow connection.
  • Generating HTML pages on demand in the server, using Java servlets or ASP pages. Again this will work with any browser, but this time you need to find an Internet service provider who allows you to run servlets or ASP pages.
  • Downloading the entire XML file to the client, and generating the display there. This has the advantage that the data is only downloaded once, and the user can then browse it at leisure, with no further interaction with the server.

    Unfortunately, at the time of writing the two major browsers (Firefox and Internet Explorer) both support XSLT 1.0 transformations, but neither yet supports XSLT 2.0. To get around this problem, I use a fallback stylesheet for this case that uses XSLT 1.0 only.

    Another disadvantage is security; you have no way of filtering the data, for example to remove details of living persons, and you have no way to stop your entire XML file being copied by the user (for example, the user can View Source or can poke around in the browser cache).

The only real difference between the three cases, as far as the stylesheet is concerned, is that the hyperlinks will be generated differently.

We'll handle the differences by writing a generic stylesheet module containing all the common code for the three cases and then importing this into stylesheets that handle the variations. But we'll start by writing a stylesheet that displays one individual on one HTML page, and then we'll worry about the hyperlinks later.

The Stylesheet

We're ready to write a stylesheet,
person.xsl
that generates an HTML page showing the information relevant to a particular individual. This stylesheet will need to accept the
Id
of the required individual as a stylesheet parameter. If no value is supplied, we'll choose the first

record in the file. Here's how it starts:

   xmlns:xsl=“http://www.w3.org/1999/XSL/Transform”

   xmlns:xs=“http://www.w3.org/2001/XMLSchema”

   xmlns:ged=“http://www.wrox.com/569090/gedcom”

   xmlns=“http://www.w3.org/1999/xhtml”

   exclude-result-prefixes=“xs ged”>




   schema-location=“http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd”/>




The stylesheet defines four namespaces: the XSLT namespace, the schema namespace, a local namespace which is used only for the functions defined in this stylesheet module, and the XHTML namespace for the result tree. The schema and
ged
namespaces aren't needed in the output file, so the
exclude-result-prefixes
attribute is set to prevent them appearing.

I've chosen to generate the output in XHTML, so I've specified
method=“xhtml”
in the

declaration, and I've imported the XHTML schema. This means that any attempt to generate incorrect XHTML can be reported immediately, while the stylesheet is running, and the offending instruction in the stylesheet can be pinpointed. I decided to use the transitional XHTML schema rather than the strict version of the schema, frankly out of laziness: the strict version is
very
strict indeed, and extra work would be needed on this stylesheet to make its output conform.

There's now a fair bit of preamble before we do any useful work. This is all designed to make the subsequent processing easier and faster. First we define some keys:







         use=“element(*,ParentType)/Link/@Ref”/>



The main purpose of the keys is to make navigation around the structure faster. For a data model like GEDCOM, with many cross-references from one record to another, this can make a big difference. The first two keys allow records to be found given their unique identifiers (they are indexed on their
Id
attributes). The other three keys are there essentially to follow inverse relationships: a family contains links to the children in the family, and the first key enables us quickly to find the family with a link to a given child (in our data there will never be more than one, though GEDCOM allows it; for example, a child may be linked both to her birth parents and to her adoptive parents).

Having defined these keys, we now define some functions to make it easier to navigate around the data.



  

  




  

  




  

  

    select=“if ($couple/HusbFath and $couple/WifeMoth)

            then (ged:events-for-person(

                       $couple/key(‘indi’, $couple/HusbFath/Link/@Ref))

                    intersect

                  ged:events-for-person(

                       $couple/key(‘indi’, $couple/WifeMoth/Link/@Ref)))

            else ()”/>


This checks that the family record does indeed identify a couple (both parents are present), and then finds all the events in which both parties participate—note the use of the
intersect
operator to find the nodes that are present in two given node-sets.



  

  

    select=“ged:events-for-person($person)[@Type=‘birth’]”/>

  

    select=“ged:events-for-person($person)[@VitalType=‘birth’]”/>   

  


This function is trying to accommodate some of the variety possible in the model. It first finds the person's birth event (there may be more than one if it has been recorded more than once). Then it selects the events whose
VitalType
is
birth
: this might include records such as baptism or the civil registration of birth (which careful genealogists will distinguish from the birth event itself). It returns the first one of these events that has a date associated with it.



  

  

                as=     “schema-element(EventRec)*”

                select= “ged:events-for-couple($couple)[@VitalType=‘marriage’]”/>

  

                as=     “schema-element(EventRec)*”

                select= “$marriage-vitals[@Type=‘marriage’]”/>

  

                as=     “element(*, DateType)?”

                select= “($marriage/Date, $marriage-vitals/Date)[1]”/>

  

                        then $marriage-date

                        else ged:sort-dates($couple/Child/Link/@Ref/key(‘indi’,.)/

                                   ged:birth-date(.))[1]”/>


Other books

Greygallows by KATHY
A Little Learning by J M Gregson
Escaping Home by A. American
Makin' Miracles by Lin Stepp