Querying XML: Use Cases - Use case REF: queries based on references.
(Page 4 of 5 )
References are an important aspect of XML. This use case describes a database in which references play a significant role and contains several representative queries that exploit these references.
Suppose that the file census.xml contains an element for each person recorded in a recent census. For each person element, the person’s name, job, and spouse (if any) are recorded as attributes. Thespouseattribute is an IDREF-type attribute that matches the spouse element’s ID-typenameattribute.
The parent-child relationship among persons is recorded by containment in the element hierarchy. In other words, the element that represents a child is contained within the element that represents the child’s father or mother. Due to deaths, divorces, and remarriages, a child might be recorded under either its father or mother (but not both). In this exercise, the term “children of X” includes “children of the spouse of X.” For example, if Joe and Martha are spouses, Joe’s element contains an element Sam, and Martha’s element contains an element Dave, then both Joe’s and Martha’s children are considered to be Sam and Dave. Each person in the census has zero, one, or two parents.
This use case is based on an input document named census.xml, with the following DTD:
<!DOCTYPE census [ <!ELEMENT census (person*)> <!ELEMENT person (person*)> <!ATTLIST person name ID #REQUIRED spouse IDREF #IMPLIED job CDATA #IMPLIED > ]>
The following census data describes two friendly families that have several intermarriages:
<xsl:template match="person[@spouse='Martha']"> <xsl:copy> <xsl:copy-of select="@*"/> </xsl:copy> </xsl:template> Question 2. Find parents of athletes: <xsl:template match="census"> <xsl:variable name="everyone" select="//person"/> <result> <!-- For each person with children --> <xsl:for-each select="$everyone[person]"> <xsl:variable name="spouse" select="$everyone[@spouse=current()/@name]"/> <xsl:if test="./person/@job = 'Athlete' or $spouse/person/@job = 'Athlete'"> <xsl:copy> <xsl:copy-of select="@*"/> </xsl:copy> </xsl:if> </xsl:for-each> </result> </xsl:template> Question 3. Find people who have the same job as one of their parents:
Try it yourself.
Question 4. List names of parents and children who have the same job, and list their jobs: <xsl:template match="census"> <xsl:variable name="everyone" select="//person"/> <result> <!-- For each person with children --> <xsl:for-each select="$everyone[person]">
<xsl:template match="person"> <xsl:param name="parent"/> <match parent="{$parent}" child="{@name}" job="{@job}"/> </xsl:template> Question 5. List name-pairs of grandparents and grandchildren: <xsl:template match="census"> <xsl:variable name="everyone" select="//person"/> <result> <!-- For each grandchild --> <xsl:for-each select="$everyone[../../../person]"> <!-- Get the grandparent1 (guaranteed to exist by for each --> <grandparent name="{../../@name}" grandchild="{@name}"/> <!-- Get the grandparent2 is grandparent1's spouse if listed --> <xsl:if test="../../@spouse"> <grandparent name="{../../@spouse}" grandchild="{@name}"/> </xsl:if> <!-- Get the names of this person's parent's spouse (i.e. their mother or father as the case may be) --> <xsl:variable name="spouse-of-parent" select="../@spouse"/> <!-- Get parents of spouse-of-parent, if present --> <xsl:variable name="gp3" select="$everyone[person/@name=$spouse-of-parent]"/> <xsl:if test="$gp3"> <grandparent name="{$gp3/@name}" grandchild="{@name}"/> <xsl:if test="$gp3/@spouse"> <grandparent name="{$gp3/@spouse}" grandchild="{@name}"/> </xsl:if> </xsl:if> </xsl:for-each> </result> </xsl:template> Question 6. Find people with no children: <xsl:strip-space elements="*"/> <xsl:template match="census"> <xsl:variable name="everyone" select="//person"/> <result> <xsl:for-each select="$everyone[not(./person)]"> <xsl:variable name="spouse" select="$everyone[@name = current()/@spouse]"/> <xsl:if test="not ($spouse) or not($spouse/person)"> <xsl:copy-of select="."/> </xsl:if> </xsl:for-each> </result> </xsl:template> Question 7. List the names of all Joe's descendants. Show each descendant as an element with the descendant's name as content and his or her marital status and number of children as attributes. Sort the descendants in descending order by number of children and secondarily in alphabetical order by name: <xsl:variable name="everyone" select="//person"/>
This example accomplishes the query, but it isn’t pretty! The complications come from the need to collect all descendants into a node set so they can be sorted. This forces the use of the node-set extension function. It also means that theid( )function will not help find the spouse because it only works relative to the node’s document. However, the nodes are copies of the original nodes and thus do not have the same document. This situation forces you to go after the spouse elements in a much more cumbersome way by searching for a variable containing all person elements. Contrast this solution to the following XQuery solution:
define function descrip (element $e) returns element { let $kids := $e/* union $e/@spouse=>person/* let $mstatus := if ($e[@spouse]) then "Yes" else "No" return <person married={ $mstatus } nkids={ count($kids) }>{ $e/@name/text() } </person> define function descendants (element $e) { if (empty($e/* union $e/@spouse=>person/*)) then $e else $e union descendants($e/* union $e/@spouse=>person/*) }