This chapter is based on Chapter 23 of Elliotte Rusty Harold's book [Harold 1999], plus additional material from the web.
Before introducing the course project, it will be instructive to work through a similar exercise in order to achieve a better understanding of what is required. Here are the basic steps.
The family tree DTD consists of three components. Figure 1 is the DTD describing an individual person.
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY % event "(place?, date?)">
<!ENTITY % reference "person IDREF #REQUIRED">
<!ELEMENT place (#PCDATA)>
<!ELEMENT date (#PCDATA)>
<!ELEMENT person (name | birth | death | father | mother |
spouse | note)*>
<!ATTLIST person id ID #REQUIRED
sex (m | f) #IMPLIED>
<!ELEMENT name (given?, surname?)>
<!ELEMENT given (#PCDATA)>
<!ELEMENT surname (#PCDATA)>
<!ELEMENT birth %event;>
<!ELEMENT death %event;>
<!ELEMENT father EMPTY>
<!ATTLIST father %reference;>
<!ELEMENT mother EMPTY>
<!ATTLIST mother %reference;>
<!ELEMENT spouse EMPTY>
<!ATTLIST spouse %reference;>
<!ELEMENT note (#PCDATA)>
|
Most importantly, each person is identified by a unique
ID; this will be invaluable for later
processing of individuals. This DTD also follows the
conventional western view that a person has one father and
one mother, one birthday, one death date, and a single name.
Dates are left vague because historical data may be vague;
additionally the sex may not be known of babies that died
young. Figure 2 brings people together as
families.
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY % person SYSTEM "person.dtd">
%person;
<!ELEMENT family (husband?, wife?, child*, marriage*,
divorce*, note*)>
<!ELEMENT husband EMPTY>
<!ATTLIST husband %reference;>
<!ELEMENT wife EMPTY>
<!ATTLIST wife %reference;>
<!ELEMENT child EMPTY>
<!ATTLIST child %reference;>
<!ELEMENT marriage %event;>
<!ELEMENT divorce %event;>
|
Note that this DTD includes the person DTD as a
SYSTEM entity. A family is generally composed
of a husband, a wife, and zero or more children. Either the
husband or the wife is optional. Separating people from
families makes maintenance easier! Finally, Figure 3 brings
together people and families as components of a family
tree.
<?xml version="1.0" encoding="UTF-8"?> <!ENTITY % family SYSTEM "family.dtd"> %family; <!ELEMENT family-tree (person | family)*> |
Note that this DTD includes the family DTD as a
SYSTEM entity. Figure 4 shows the (incomplete)
Farmer family tree as a valid XML document.
<?xml version="1.0"?>
<!DOCTYPE family-tree SYSTEM "family-tree.dtd">
<family-tree>
<person id="p5" sex="m">
<name>
<given>Alfred Ernest</given>
<surname>Farmer</surname>
</name>
</person>
<person id="p6" sex="m">
<name>
<given>Ronald Alfred</given>
<surname>Farmer</surname>
</name>
<birth>
<place>London</place>
<date>27 April, 1922</date>
</birth>
<father person="p5"/>
</person>
<person id="p7" sex="f">
<name>
<given>Daisy</given>
<surname>Farmer</surname>
</name>
</person>
<person id="p8" sex="m">
<name>
<given>Stanley</given>
<surname>Small</surname>
</name>
<birth>
<place>Catford, London</place>
<date>11 April, 1924</date>
</birth>
</person>
<person id="p9" sex="f">
<name>
<given>Nansi</given>
<surname>Small</surname>
</name>
<birth>
<place>London</place>
<date>17 January, 1929</date>
</birth>
</person>
<person id="p10" sex="m">
<name>
<given>Michael Ronald</given>
<surname>Farmer</surname>
</name>
<birth>
<place>Queen Charlotte's Hospital, London</place>
<date>10 November, 1945</date>
</birth>
<father person="p6"/>
<mother person="p7"/>
<spouse person="p11"/>
</person>
<person id="p11" sex="f">
<name>
<given>Susan Anne</given>
<surname>Small</surname>
</name>
<birth>
<place>Brisbane, Australia</place>
</birth>
<father person="p8"/>
<mother person="p9"/>
<spouse person="p10"/>
</person>
<person id="p12" sex="m">
<name>
<given>Robert</given>
<surname>Small</surname>
</name>
<father person="p8"/>
<mother person="p9"/>
</person>
<family>
<husband person="p6"/>
<wife person="p7"/>
<child person="p10"/>
</family>
<family>
<husband person="p8"/>
<wife person="p9"/>
<child person="p11"/>
<child person="p12"/>
</family>
<family>
<husband person="p10"/>
<wife person="p11"/>
<marriage>
<place>Lewisham, London</place>
<date>12 April, 1990</date>
</marriage>
</family>
</family-tree>
|
If this XML document is transformed by the default rule for the root node, then the result is anodyne, as shown in Figure 5. Only the text nodes are output by default.
Alfred Ernest
Farmer
Ronald Alfred
Farmer
London
27 April, 1922
Daisy
Farmer
Stanley
Small
Catford, London
11 April, 1924
Nansi
Small
London
17 January, 1929
Michael Ronald
Farmer
Queen Charlotte's Hospital, London
10 November, 1945
Susan Anne
Small
Brisbane, Australia
Robert
Small
Lewisham, London
12 April, 1990
|
Note that this output file has been pruned of most of its whitespace. Even so, it doesn't make sense unless you know the original format. The default XSLT needs to be augmented to include extra information (what are we describing) and hyperlinks (to relatives) in whatever markup language we're using (HTML in this case). The following link shows one possible version of the Farmer family tree as a stand-alone HTML document. Figure 6 is Mick's final XSLT document that processes the XML document starting with the document root to generate the document linked above.
<?xml version="1.0"?>
<!-- family2html -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<HTML>
<HEAD>
<TITLE>Family Tree</TITLE>
</HEAD>
<BODY>
<xsl:apply-templates select="family-tree"/>
</BODY>
</HTML>
</xsl:template>
<xsl:template match="family-tree">
<H1>Family Tree</H1>
<H2>Families</H2>
<xsl:apply-templates select="family"/>
<H2>People</H2>
<xsl:apply-templates select="person"/>
</xsl:template>
<xsl:template match="family">
<UL>
<xsl:apply-templates select="husband"/>
<xsl:apply-templates select="wife"/>
<xsl:apply-templates select="child"/>
</UL>
</xsl:template>
<xsl:template match="husband">
<LI>Husband:
<A href="#{@person}">
<xsl:value-of select="id(@person)/name"/>
</A>
</LI>
</xsl:template>
<xsl:template match="wife">
<LI>Wife:
<A href="#{@person}">
<xsl:value-of select="id(@person)/name"/>
</A>
</LI>
</xsl:template>
<xsl:template match="child">
<LI>Child:
<A href="#{@person}">
<xsl:value-of select="id(@person)/name"/>
</A>
</LI>
</xsl:template>
<xsl:template match="person">
<H3>
<A name="{@id}"/>
<xsl:value-of select="name"/>
</H3>
<UL>
<xsl:if test="birth">
<LI>Born: <xsl:value-of select="birth"/></LI>
</xsl:if>
<xsl:if test="death">
<LI>Died: <xsl:value-of select="death"/></LI>
</xsl:if>
<xsl:apply-templates select="father"/>
<xsl:apply-templates select="mother"/>
</UL>
<P>
<xsl:apply-templates select="note"/>
</P>
</xsl:template>
<xsl:template match="father">
<LI>
<A href="#{@person}">Father</A>
</LI>
</xsl:template>
<xsl:template match="mother">
<LI>
<A href="#{@person}">Mother</A>
</LI>
</xsl:template>
</xsl:stylesheet>
|
The most important aspect of this structure is that individual persons have unique IDs; this enables the style sheet to locate individual person's details. It also means that the family tree must be complete, i.e. every person referenced must be included as an individual person.
Note that not all of the information is made available with this transformation. Feel free to augment these transformations by providing every piece of information provided in the XML document. Also, consider how to generate a more formal and traditional family tree. It's not easy, especially in raw HTML!