Getting started with XSLT and XPath (II)
August 23, 2000
The following more complex examples are meant merely as illustrations of some of the powerful facilities and techniques available in XSLT. These samples expose concepts such as variables, functions, and process control constructs a stylesheet writer uses to effect the desired result, but does not attempt any tutelage in their use.
This subsection can be skipped entirely, or, for quick exposure to some of the facilities available in XSLT and XPath, only briefly reviewed. In the associated narratives, I've avoided the precise terminology that hasn't yet been introduced and I overview the stylesheet contents and processor behaviors in only broad terms. Subsequent subsections of this chapter review some of the basic terminology and design approaches.
I hope not to frighten the reader with the complexity of these examples, but it is important to realize that there are more complex operations than can be illustrated using our earlier three-line source file example. The complexity of your transformations will dictate the complexity of the stylesheet facilities being engaged. Simple transformations can be performed quite simply using XSLT, but not all of us have to meet only simple requirements.
The following XML source information in
prod.xml is used to produce two very
Lines 2 through 11 describe the document model for the sales information. Lines 14
summarize product description information and have unique identifiers according to
IDREF rules. Lines 16 through 23 summarize customer purchases
(product sales), each entry referring to the product having been sold by use of the
idref= attribute. Not all customers have been sold all products.
Consider the following two renderings of the same data using two orientations, each produced with different stylesheets:
Note how the same information is projected into a table orientation on the left canvas and a list orientation on the right canvas. The one authored order is delivered in two different presentation orders. Both results include titles from boilerplate text not found in the source. The table information on the left includes calculations of the sums of quantities in the columns, generated by the stylesheet and not present explicitly in the source.
The implicit stylesheet
prod-imp.xsl is an XHTML file utilizing the XSLT
vocabulary for instructions to fill in the one result template by pulling data from
Recall that a stylesheet is oriented according to the desired result, producing the
in result parse order. The entire document is an HTML file whose document element
line 3 and ends on line 30. The XSLT namespace and version declarations are included
document element. The naming of the document element as "html" triggers the default
HTML result tree serialization conventions. Lines 5 and 6 are fixed boilerplate information
for the mandatory
Lines 7 through 29 build the result table from the content. A single header row
<th> is generated in lines 9 through 12, with the columns of that row
generated by traversing all of the
<product> elements of the source. The
focus moves on line 11 to each
<product> source element in turn and the
markup associated with the traversal builds each
<td> result element. The
content of each column is specified as "
.", which for an element evaluates to
the string value of that element.
Having completed the table header, the table body rows are then built, one at a time
<cust> child of a
<record> child of
<sales> child of the root of the document, according to the XPath
/sales/record/cust". The current focus moves to the
<cust> element for the processing on lines 15 through 21. A local scope
variable is bound on line 15 with the tree location of the current focus (note how
instruction uses the same XPath expression as on line 11 but with a different result).
table row is started on line 16 with the leftmost column calculated from the
num= attribute of the
<cust> element being processed.
The stylesheet then builds in lines 17 through 20 a column for each of the same columns
created for the table header on line 10. The focus moves to each product in turn for
processing of lines 18 through 20. Each column's value is then calculated with the
$customer/prodsale[@idref=current()/@id]", which could be expressed
as follows "from the customer location bound to the variable
all of the
<prodsale> children of that customer, find that child whose
idref= attribute is the value of the
id= attribute of the focus
element." When there is no such child, the column value is empty and processing continues.
As many columns are produced for a body row as for the header row and our output becomes
Finally, lines 23 through 28 build the bottom row of the table with the totals calculated
for each product. After the boilerplate leftmost column, line 24 uses the same
//product" expression as on lines 10 and 17 to generate the same number of
table columns. The focus changes to each product for lines 25 through 28. A local
variable is bound with the focus position in the tree. Each column is then calculated
a built-in function as the sum of all
<prodsale> elements that reference
the column being totaled. The XPath designers, having provided the
function in the language, keep the stylesheet writer from having to implement complex
counting and summing code; rather, the writer merely declares the need for the summed
to be added to the result on demand by using the appropriate XPath expression.
prod-exp.xsl is an explicit XSLT stylesheet with a number of result
templates for handling source information:
The document element on line 3 includes the requisite declarations of the language namespace and the version being used in the stylesheet. The children of the document element are the template rules describing the source tree event handlers for the transformation. Each event handler associates a template with an event trigger described by an XPath expression.
Lines 6 through 10 describe the template rule for processing the root of the document,
indicated by the "
/" trigger in the
match= attribute on line 6.
The result document element and boilerplate is added to the result tree on lines 7
Line 9 instructs the XSLT processor in
<xsl:apply-templates> to visit all
<record> element children of the
element, as specified in the
select= attribute. For each location visited, the
processor pushes that location through the stylesheet, thus triggering the template
result markup it can match for each location.
Lines 12 and 13 describe the result markup when matching a
element. The focus moves to the
<record> element being visited. The
template rule on line 13 adds the markup for the HTML unordered list
element to the result tree. The content of the list is created by instructing the
to visit all children of the focus location (implicitly by not specifying any
select= attribute) and apply the templates of result markup it triggers for
each child. The only children of
The stylesheet does not provide any template rule for the
element, so built-in template rules automatically process the children of each location
being visited in turn. Implicitly, then, our source information is being traversed
depth-first order, visiting the locations in parse order and pushing each location
any template rules that are then found in the stylesheet. The children of the
<cust> elements are
The stylesheet does provide a template rule in lines 15 through 20 to handle a
<prodsale> element when it is pushed, so the XSLT processor adds the
markup triggered by that rule to the result. The focus changes when the template rule
handles it, thus, lines 16, 18, and 20 each pull information relative to the
<prodsale> element, respectively: the parent's
<cust> element's attribute); the string value of the
target element being pointed to by the
idref= attribute (indirectly obtaining the
element's value); and the value of the
<prodsale> element itself.
This is a prose version of an excerpt from the book "Practical Transformation Using XSLT and XPath" (Eighth Edition ISBN 1-894049-05-5 at the time of this writing) published by Crane Softwrights Ltd., written by G. Ken Holman; this excerpt was edited by Stan Swaren, and reviewed by Dave Pawson.