Mark Wilson I am the creator of TopXML. I am available for international and local (Australia) contracts. I am a Solution Architect/Business Analyst. I have worked in IT in several countries (NZ, Australia, South Africa, UK) building and training teams for government and very large non-governmental organizations. I am ex-Microsoft Consulting Services. I wrote the first book on Microsoft XML published in 2000 called XML Programming with VB and ASP. Most recently I have been building tools for the SEO industry. Ask me for a 37 point SEO health-checkup for your website.
First posted :
12/05/2003
Times viewed :
295
Building Oracle XML Applications
By Steve Muench
September 2000
1-56592-691-9, Order Number: 6919
810 pages, $44.95, Includes CD-ROM
We've used XSLT stylesheets in previous chapters to
transform database-driven XML into HTML pages, XML datagrams of a particular
vocabulary, SQL scripts, emails, and so on. If you're a developer trying to
harness your database information to maximum advantage on the Web, you'll find
that XSLT is the Swiss Army knife you want permanently attached to your belt.
In a world where the exchange of structured information is core to your
success, and where the ability to rapidly evolve and repurpose information is
paramount, Oracle XML developers who fully understand how to exploit XSLT are
way ahead of the pack.
XSLT 1.0 is the W3C standard language for describing
transformations between XML documents. It is closely aligned with the
companion XPath 1.0 standard and works in concert with it. As we'll see in
this chapter, XPath let's you say what to transform, and XSLT provides the
complementary language describing how to carry out the transformation. An XSLT
stylesheet describes a set of rules for transforming a source XML document
into a result XML document. An XSLT processor is the software that carries out
the transformation based on these rules.
In the simple examples in previous chapters, we have seen
three primary ways to use the Oracle XSLT processor. We've used the oraxsl
command-line utility, the XSLT processor's programmatic API, and the
<?xml-stylesheet?> instruction to associate a stylesheet with an XSQL
page. In this chapter, we begin exploring the full power of the XSLT language
to understand how best to use it in our applications.
XSLT Processing Mechanics
An XSLT stylesheet describes a transformation that operates on
the tree-structured infoset of a source XML document and produces a tree of
nodes as its output.
A transformation of this document operates on the document's
corresponding node tree (shown in Figure
7-1). The tree of nodes for an XML document always starts with a root node
that represents the document itself. Child nodes of the root can be the single
document element node--<ROWSET>, in our example--as well as comments and
processing instructions. Child nodes of the document element can be any
combination of text nodes and element nodes, each of which may, in turn, have
similar child nodes. This nesting of nodes forms a tree.
Figure 7-1. Node tree for a
simple ROWSET document
TIP:
Remember that an XML document can look like this:
<ROWSET>
<ROW num="1">
<X>Y</X>
</ROW>
</ROWSET>
or it can look like this:
<ROWSET><ROW
num="1"><X>Y</X></ROW></ROWSET>
NOTE: While both expressions contain a logically equivalent
element structure, the former example contains additional whitespace
(denoted by WS nodes in Figure
7-1) to give it that indented look. Specifically, it contains a carriage
return at the end of every line followed by a series of spaces at the start
of the next line. When considering an XML document as a tree of nodes, don't
forget that the text nodes containing whitespace also count as nodes the
same as text like 7788 or SCOTT. Since you can't see it, whitespace is easy
to forget about.
To carry out a transformation, an XSLT processor requires two
ingredients:
The source tree of nodes
An XSLT stylesheet containing a set of transformation
rules
An XSLT stylesheet is an XML document that uses elements from
the XSLT vocabulary to describe a transformation. The document element of
every stylesheet is an <xsl:stylesheet> element whose content is a set
of rules describing the transformation to be performed. Each rule in the
stylesheet contains an associated XPath pattern that matches the nodes in the
source document to which that rule should apply. Each rule is called a
template and is represented by an <xsl:template> element with a
match="pattern" attribute for its associated XPath match pattern.
For example, a rule like this:
<xsl:template match="/">
<!-- Some Result Content: Elements, Attributes, Text, etc. -->
</xsl:template>
applies to the root node of the document, matching the XPath
pattern "/".
Similarly, a rule like this:
<xsl:template match="ROWSET/ROW[ENAME]">
<!-- Some Result Content: Elements, Attributes, Text, etc. -->
</xsl:template>
applies only to <ROW> elements in the source document
that have an <ENAME> child element and occur as immediate children of a
<ROWSET> element.
Each rule is called a template because the literal elements
and attributes contained inside the body of the rule act as a blueprint for
constructing a part of the result tree. The XSLT processor constructs the
content of a rule's template in the result tree whenever it processes a source
node matching the rule's pattern. Figure
7-2 illustrates what happens when a rule like this:
is triggered by processing a <ROW> element in the source
tree that matches the XPath pattern ROWSET/ROW[ENAME].
Figure 7-2. Matching source
tree node and constructing result fragment
As the matching template is instantiated, the following three
things occur:
Literal result elements and attributes in the
template are created in the result tree. Result elements and attributes
that are not from the XSLT namespace are considered "literal"
since they are constructed as is in the result tree. In the example just
given, the <Employee> element and its id attribute are created.
Any attribute value templates of the form {XPathExpr}
contained within literal attribute values are replaced by the value of
their XPath expression. In the example, the {EMPNO} inside the literal
attribute value NX-{EMPNO} is replaced by the value of the EMPNO XPath
expression. This evaluates to 7839, so the final value for the id
attribute in the result tree is NX-7839.
Any elements in the XSLT namespace are processed in
document order. The <xsl:value-of> element is processed and is
replaced by a text node containing the string value of the XPath
expression in its select attribute--in this case, KING.
The basic operation can be summarized as follows: when a node
in the source matches a rule's pattern, the content of that rule is created in
the result tree. Once you grasp this basic operation, the overall XSLT
processing model is easy to understand. Given a source tree and a stylesheet,
the XSLT processor carries out the transformation described by rules in the
stylesheet by following a sequence of steps, just like the ones we have
described.
A list of nodes in the source tree is processed to create a
portion, or "fragment," of the result tree. The result tree fragment
for the list of nodes is created by processing the nodes in order and
concatenating each of their respective result tree fragments together in the
same order. The node in the current node list being processed is known, not
surprisingly, as the current node. The current node is processed by
considering the set of all possible rules that match it and then selecting the
single rule that matches it best. Only a single rule is ever used to process
the current node in the current node list.
To start the process, the XSLT processor begins with a node
list containing only the document root. It finds the template matching this
root node--typically the rule with match="/"--and instantiates the
contents of the template in the result tree, following the three basic
processing steps to complete the job. If the template contains elements from
the XSLT namespace that select other nodes to process, the sequence of
matching and template content instantiation continues recursively until there
are no nodes left to process. When processing is completed, the result tree
represents the target document produced by the transformation.
Single-Template Stylesheets
Many useful transformations can be expressed with just a
single-root template. We'll examine the single-template stylesheet here, but
we'll spend the rest of this chapter learning why there's a world beyond the
root template and why it's worth learning about.
All of the stylesheets we've seen so far for transforming XML
into HTML either have looked like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- The "root" or "main" template -->
<xsl:template match="/">
<html>
<body>
<!--
| Literal result elements and attributes, intermingled with
| <xsl:for-each>, <xsl:value-of>, attribute value templates, etc.
+-->
</body>
</html>
<xsl:template>
</xsl:stylesheet>
or have used the simple form of the single-root template
stylesheet, which looks like this:
<!-- In the "simple form" of a stylesheet, the root template is implied -->
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<body>
<!--
| Literal result elements and attributes, intermingled with
| <xsl:for-each>, <xsl:value-of>, attribute value templates, etc.
+-->
</body>
</html>
TIP:
When you see the xsl namespace declaration:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
it is natural to think that the XSLT processor will try to
access that URL when your stylesheet is processed. However, the declaration
is only used as a unique string to identify the namespace for XSLT. If you
do not provide this exact string as the namespace URI for the xsl namespace
prefix, the XSLT processor will simply ignore <xsl:template>, <xsl:for-each>,
<xsl:value-of>, and other elements with the xsl prefix since it will
not recognize them as XSLT actions.
This transforms our simple Emp.xml <ROWSET> document
into an HTML document with the employee data in a table, as shown in Figure
7-3.
Figure 7-3. Employee data in
an HTML table
The content of the root template is a mixture of the familiar
literal HTML elements <html>, <body>, <table>, <tr>,
and <td>, strategically sprinkled with <xsl:for-each> and <xsl:value-of>
elements. When the XSLT processor instantiates the root template, the document
root node is the current node. The <xsl:for-each> element:
Selects a list of source tree nodes to process
Makes this list of selected nodes the current node
list
Begins processing the nodes in the current node list
in order
The content of the <xsl:for-each> element is
instantiated in the result tree for each node in the current node list. The
content of this instantiated result tree fragment is processed (with respect
to the current node) for additional XSLT elements, if any.
Any <xsl:value-of> elements encountered in the
instantiated result tree fragments are replaced by the string value of the
XPath expression in their select attribute. Figure
7-4 illustrates the process.
Figure 7-4. Understanding a
single-template stylesheet
The resulting HTML document is shown in Example
7-2.
Example 7-2:Output of Emp.xml Using
Single-Root Template Stylesheet
In this example, the XSLT processor only performs template
matching for the root node. All subsequent nodes selected for processing are
the result of processing the <xsl:for-each> action's select patterns and
iterating over the node-sets they return.
If a stylesheet uses only the root template, then it can
optionally use the simple-form stylesheet syntax that allows <xsl:stylesheet>
and <xsl:template match="/"> to be left out. In this case, the
literal element that would have been the first element in the root template is
instead the first element of the entire stylesheet. You must include the
namespace declaration for the XSLT namespace on the literal result element
that is now the document element of the stylesheet document, as well as add
the namespace-qualified xsl:version="1.0" attribute to the element:
This produces the same results as the stylesheet with normal
syntax we saw earlier.
Understanding Input and Output Options
The XSLT transformation process described earlier was
explained in terms of node trees. These trees of nodes are the logical form
that both the source and result of a transformation can take on the
"inside" of a transformation being performed by an XSLT processor.
However, this is what happens on the outside:
The source document typically begins as a stream of
human-readable characters.
The result of the transformation typically needs to
be written out as another stream of human-readable characters--for
example, to send the result back to a requesting browser or to save the
result in a file for later.
The input to an XSLT transformation must be a tree of source
nodes produced by either parsing a well-formed XML document or creating the
tree programmatically (for example, via DOM or SAX APIs).
All XSLT transformations process the source node tree to
produce a tree of result nodes. If multiple transformations are being applied
in sequence by your application, the result tree of one transformation becomes
the source tree of the next transformation in sequence. When no more
transformations need to be done, the final tree of result nodes needs to be
written out as a stream of characters again. This process is called
serializing the result tree.
Simple-form stylesheets take advantage of default
serialization rules described by the XSLT 1.0 specification to make common
cases simple. They serialize transformed output in the default UTF-8 character
set and support either of the following output formats:
Indented, properly formatted HTML output, with a
media type of text/html
Non-indented XML output with no DOCTYPE and a media
type of text/xml
Going beyond these defaults requires using the more verbose,
standard XSLT stylesheet syntax that begins with an <xsl:stylesheet>
element that includes as a direct child an <xsl:output> element, which
offers control over the serialization process.
The most important serialization control to understand is the
output method. This governs the basic rules that the XSLT processor will use
when serializing the result tree nodes to an output stream. XSLT 1.0 supports
three different output methods:
<xsl:output method="xml"/>
This method is the default and outputs the nodes as
well-formed XML.
<xsl:output method="html"/>
This method is the default for result trees whose
document element is <html>, <HTML>, or any case-variation in
between. It serializes elements and attributes in an HTML 4.0-friendly way
that ensures existing browsers will recognize it. In particular, it does
not write out well-formed XML.
<xsl:output method="text"/>
This method outputs only the text nodes in the
result tree in document order. It is used for transforming XML into
programming language source files, emails, or other plain text output.
Remember that XSLT stylesheets are well-formed XML
documents, so characters that need to be escaped (like & and <) must
be escaped with & and < in your stylesheets, too. While the
> entity exists to escape the > character, its use is optional.
Finally, note that a numerical character entity like & can be used
as an alternative to represent the character whose Unicode number in decimal
is 38, which is the ampersand, and some processors choose to emit all
reserved characters using this numerical approach. In your own stylesheets,
if you are more comfortable with hexadecimal, you can use a hexadecimal
numerical entity as well. For example, a carriage return, Unicode number 10
or 0A in hex, can be represented alternatively as using decimal or

 using hex.
The following stylesheet uses the html output method and
transforms the <ROWSET> document into a simple HTML page with a
paragraph tag and an image:
Finally, this third example stylesheet uses the text output
method to transform the <ROWSET> document into plain text output with no
markup tags:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:text>Hello </xsl:text>
<xsl:value-of select="ROWSET/ROW/ENAME"/>
<xsl:text> & Family,
</xsl:text>
<xsl:text>Your id is </xsl:text>
<xsl:value-of select="ROWSET/ROW/EMPNO"/>
</xsl:template>
</xsl:stylesheet>
This produces the result:
Hello King & Family,
Your id is 7839
Note that we're using <xsl:text> elements to include
literal text in the result of the transformation. In general, whitespace is
ignored in the stylesheet document, so tags can be nicely indented for
readability. However, the whitespace inside of <xsl:text> elements is
respected, so we use <xsl:text> when we want precise control over the
whitespace that gets created in the resulting document. Literal spaces, tabs,
and carriage returns included in <xsl:text> elements are included
verbatim in the result. Note the use of &x0A; to represent the literal
carriage return in the result.
Figure
7-5 illustrates the source document, source node tree, result node trees,
and final serialization of the previous three transformations, abiding by each
transformation's specified output method.
Figure 7-5. Understanding
how XSLT output methods affect serialization
In addition to the output method, several other interesting
serialization hints can be specified on the <xsl:output> element in a
stylesheet. Table
7-1 summarizes the <xsl:output> settings that come in handy most
frequently.
With the fundamentals of single-root template transformations
and their most common serialization options now under our belts, let's move on
to understand why developers ever bother to use more than just a single-root
template.
Improving Flexibility with Multiple
Templates
As we've learned, a stylesheet is a set of rules. When you use
only a single-root template, your stylesheet has, accordingly, only a single
rule: "When you see the root of the source document, do everything inside
this!"
As we'll learn in this section, this strategy has pros and
cons similar to those of adopting an "everything in a single main( )
method" coding style in Java:
public class doit {
public static void main( ) (String[] args) {
// When this program runs, do everything inside this!
}
}
Developers learning Java find it easy to start with this
simple approach, but they quickly find themselves writing repetitive code that
would be nice to factor into helper methods. When this occurs, they would like
to stand on the shoulders of other developers by extending others' existing
work, overriding just the methods that need to behave differently.
We'll see that there is a nice conceptual analogy between
methods in Java classes and templates in XSLT stylesheets. In Java, methods
are both the unit of behavior and the unit of overriding. If you write a class
with all of the programming logic in a single main( ) method, then someone
extending your class can only override that single main( ) method. This means
they have to rewrite all the logic just to change one small behavioral aspect.
The more effectively a class's methods are logically factored to represent the
set of subtasks the class must perform, the easier it is to reuse a single
useful method when appropriate, and the easier it is to override just a part
of the existing behavior, if necessary.
In XSLT, templates are the unit of behavior as well as the
unit of overriding. Similar to the Java analogy above, if you write a
stylesheet with all of the transformation logic in a single-root template,
then someone extending your stylesheet can only override that entire template
to change the way your transformation behaves. The more effectively a
stylesheet's templates are logically factored to reflect the individual
transformation tasks to be performed, the easier it is to reuse a single
useful template when appropriate, and the easier it is to override just a part
of the existing transformation behavior, if necessary.
Using Multiple Templates
Example
7-3 shows what our single-root template stylesheet from the previous
section looks like if we factored it into multiple templates. We've created a
template for each element in the source document that we will encounter and we
have made each template responsible for a small part of the transformation
job. Each template uses the <xsl:apply-templates> action to tell the
XSLT processor to "carry on processing my children nodes" so
recursive processing of the tree can continue.
Example 7-3:Simple Stylesheet to
Produce HTML Using Multiple Templates
Whenever we match a <ROWSET> element in the source tree, construct a
<table> element in the result tree to contain the results of
processing the children of the current <ROWSET>, and go process those
children now!
When the XSLT processor encounters an <xsl:apply-templates>
action, it processes the current node's children and includes any result tree
fragments constructed by that processing at the location in the result tree
where the <xsl:apply-templates> appears. Accordingly, since here <xsl:apply-templates>
is nested inside the literal <table> result element, the result of
processing the children of the current <ROWSET> element will be nested
inside the <table> element in the result tree.
You can read the entire stylesheet in Example
7-3 as shown in the following table:
When we match
Construct
The source document's root "/"
<html> element and nested <body>
element in the result tree to contain the results of processing the
document--that is, the children of the root
<ROWSET> element
<table> to contain the results of processing
the current <ROWSET>'s child nodes
<ROW> element
<tr> to contain the results of processing
the current <ROW>'s child nodes
<EMPNO> element
<td> to contain the results of processing
the current <EMPNO>'s child nodes
<ENAME> element
<td> to contain the results of processing
the current <ENAME>'s child nodes
Figure
7-6 illustrates the process that takes place during the transformation.
Figure 7-6. Transforming a
source document using multiple templates
As usual, the processor begins by processing the root node in
the source tree and finding a rule that matches it. Our stylesheet has a
match="/" template, so it is instantiated with the root node as the
current node. The root template constructs the <html> and <body>
elements, and then the <xsl:apply-templates> is executed to process the
list of children of the document root. The list of children of the root
includes one comment node and one element node, the <ROWSET>. To
construct the result tree fragment for this list of nodes, the processor
processes each one in order. The comment node is ignored (we'll learn why in a
minute) and then the <ROWSET> element is processed by finding a rule
that matches it. Our match="ROWSET" template matches, so the
processor instantiates it in the result tree. This creates a literal
<table> element in the result tree nested inside the previously
instantiated <html> and <body> elements; then <xsl:apply-templates>
is executed to process the list of children of the current <ROWSET>
element. The children of the current <ROWSET> element are the following
four nodes, listed here in order:
Text node containing whitespace
<ROW> element
Text node containing whitespace
<ROW> element
Each node in the current node list is processed by finding a
matching template and instantiating it. This has the effect of copying the
whitespace to the result tree and instantiating the content of the
match="ROW" template twice to construct two <tr> elements in
the result tree. And the process continues.
The result of this transformation is the same as the result of
our single-root template stylesheet, but as we'll see in the next several
examples, having things broken into multiple templates makes for a much more
powerful paradigm.
Understanding Built-in Templates
Before moving on, we need to understand why comments were
ignored and how whitespace and the text nodes for 7839, KING, 7788, and SCOTT
found their way into the result tree.
Both of these results occurred based on the following built-in
templates that are included by the XSLT processor as part of every stylesheet:
The following table shows how to read these templates:
When we match
Construct
The source document's root "/"
or any element "*"
Nothing, but continue by processing the children
nodes of the current node
A text node "text( )"
or an attribute "@*"
A text node containing the value of the current
node, effectively copying the text or attribute value to the result
tree
A processing instruction
or a comment
Nothing
These built-in rules serve as fallbacks to keep the recursive
processing going in case the current node is not explicitly matched by any
other template in the stylesheet. Their definitions reveal a couple of
interesting points:
A rule can match any one of several patterns by using
the XPath union operator "|" between the patterns in its match
attribute.
To create a rule that matches a pattern and
explicitly does nothing--that is, creates no result tree nodes and does
not continue processing to its children--just define an empty <xsl:template>
node.
To better understand the built-in rules, let's try to
transform our simple Emp.xml document using the following stylesheet that
contains no <xsl:template> rules:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- This stylesheet contains no rules -->
</xsl:stylesheet>
We can use the oraxsl command-line tool with the command:
oraxsl Emp.xml OnlyBuiltinRules.xsl
to get the following result:
<?xml version = '1.0' encoding = 'UTF-8'?>
7839
KING
7788
SCOTT
The built-in rule for matching elements and the document root
keeps the recursion going without constructing any elements in the result
tree. Each element that is encountered matches this rule, which immediately
says "process the children of the current node." When those children
nodes are the text nodes containing whitespace, or the text nodes containing
7839, KING, 7788, and SCOTT, the built-in rule for text( ) is matched, and its
default action is to do <xsl:value-of select="."/>, which
copies the string value of the current node--the text node, in this case --to
the result tree. Accordingly, the result tree is just a pile of all the text
nodes in the document at any level, in document order. Although this is
interesting, and helpful to remember for debugging, we won't likely be putting
our empty stylesheet into production use any time soon.
Wildcard Matching and Whitespace
Handling
Let's turn our attention back to the multiple-template
stylesheet from Example
7-3. One of the things that should bother you about it is that both of the
following templates:
are doing the same thing. They each match an element that we
expect to be a child element of the <ROW> and create a table cell
<td> element to contain the result of processing the children. The
following query produced the simple Emp.xml document:
SELECT empno, ename
FROM emp
WHERE ename in ('KING','SCOTT')
ORDER BY SAL
But what if we included all of the columns in the emp table?
Would we have to perpetuate these repetitive templates to cover each new
element, like <SAL>, <COMM>, <DEPTNO>, and so on? We could,
but we should start getting the feeling that there must be a better way. Since
we want to do the same thing for every element that occurs as a child of the
<ROW>, namely, construct a <td> table cell to contain the result
of processing its children, we can simply use XPath to say exactly what we
want. The pattern to match any element that is a child of a <ROW>
element is ROW/*. So we can eliminate all of the templates for each individual
<ROW> child element and create a more generic template to the job:
<!-- Match any element child of a ROW -->
<xsl:template match="ROW/*">
<td><xsl:apply-templates/></td>
</xsl:template>
This leaves us the with the stylesheet in Example
7-4, which is ready to accommodate future expansion in the number of
columns by processing any child elements of a <ROW> in a generic way.
Example 7-4:Stylesheet Using
Multiple Templates for ROWSET/ROW Data
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--
| EmpUsingRowStar.xsl:
| Transform Emp.xml Into <table> using ROW/* to handle any column
+-->
<xsl:template match="/">
<html>
<body><xsl:apply-templates/></body>
</html>
</xsl:template>
<xsl:template match="ROWSET">
<table border="1" cellspacing="0"><xsl:apply-templates/></table>
</xsl:template>
<xsl:template match="ROW">
<tr><xsl:apply-templates/></tr>
</xsl:template>
<!-- Match any element child of a ROW -->
<xsl:template match="ROW/*">
<td><xsl:apply-templates/></td>
</xsl:template>
</xsl:stylesheet>
This should produce the same result as before, so let's try
it. Using the command-line oraxsl tool to transform our Emp.xml document using
EmpUsingRowStar.xsl with the command:
But wait. This does not look exactly the same as the nicely
indented output we saw in Example
7-2 using the single-root template stylesheet from Example
7-1. The indenting of the <tr> elements and closing </table>
tag is wrong, for some reason. It's important to understand why, since it
relates to how XSLT handles whitespace in the source document. Recall that
what makes the Emp.xml document look indented is whitespace characters, like
carriage returns and spaces. Figure
7-7 illustrates what the document would look like if we could see these
whitespace characters.
Figure 7-7. Emp.xml
document with whitespace highlighted
When the template matching <ROWSET> is processed in
EmpUsingRowStar.xsl, it constructs the <table> tag and continues
recursive processing of <ROWSET>'s child nodes with <xsl:apply-templates>.
Recall from Figure
7-1 that the first-level child nodes of <ROWSET> are the following,
listed here in order:
A text node containing the whitespace characters to
indent the line: carriage return, space, space
A <ROW> element
A text node containing the indentation whitespace
characters: carriage return, space, space
A <ROW> element
Using the multiple-template approach, the XSLT processor
processes these child nodes of <ROWSET>, in order and tries to find
templates that match. When processing the first text node child, no explicit
templates in EmpUsingRowStar.xsl match this text node, so the built-in
template matching "text( )|@*" matches as a fallback and performs
its built-in action of copying the text to the result tree. There is nothing
special about how whitespace-only text nodes are handled by the built-in rule:
the characters are simply copied verbatim to the result like any text node.
These extra carriage returns copied as is into the result by the built-in
template explain why the indenting behavior of the output was slightly
disturbed.
It's equally important to understand why our stylesheet in Example
7-1 did not run into this problem. Using that single-root template
stylesheet, the XSLT processor does template matching only for the root node.
After this, the only nodes that are processed are the ones explicitly selected
by actions like <xsl:for-each>. Since that stylesheet never explicitly
selected any text nodes for processing, the problem of copying their contents
to the result never materialized.
To remedy the situation for EmpUsingRowStar.xsl, we can
instruct the XSLT processor to strip, and hence ignore for transformation, any
text nodes in the source tree that consist entirely of whitespace characters.
We can accomplish this by adding an <xsl:strip-space> element to the top
level of our stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--
| Strip text node children consisting entirely of whitespace for
| all elements in the source document.
+-->
<xsl:strip-space elements="*"/>
The value of the elements attribute of <xsl:strip-space>
is a whitespace-separated list of element names whose text node children
consist entirely of whitespace you would like to strip. Using an asterisk (*)
strips space from all elements. To strip space from all but one or all but a
few elements, you can use <xsl:strip-space> in combination with the
companion <xsl:preserve-space> element which takes an analogous elements
attribute, listing elements for which you want to preserve whitespace. By
default, an XSLT processor preserves whitespace child nodes from all elements
in the source document.
With this issue sorted out, let's build the following simple
XSQL page to test EmpUsingRowStar.xsl on live database data:
<?xml version="1.0"?>
<!-- Emp.xsql -->
<xsql:query connection="xmlbook" xmlns:xsql="urn:oracle-xsql">
SELECT empno, ename, sal, deptno
FROM emp
ORDER BY ename DESC
</xsql:query>
The query in this page includes a couple of extra columns and,
instead of just returning the rows for `KING' and `SCOTT', will return all the
rows in the emp table. As we saw in Chapter 3, Combining XML and Oracle, we
can reuse the data from the Emp.xsql page by including it in other XSQL pages
with the <xsql:include-xsql> action. In this way, we can apply different
stylesheets to the same data page produced. This will come in handy to test
the various stylesheets we develop in the rest of this chapter.
For example, we can create an EmpUsingRowStar.xsql page that
includes Emp.xsql and associates the EmpUsingRowStar.xsl stylesheet to it. The
page looks like this:
<?xml version="1.0"?>
<!-- EmpUsingRowStar.xsql -->
<?xml-stylesheet type="text/xsl" href="EmpUsingRowStar.xsl"?>
<!-- Include Emp.xsql and style it with EmpUsingRowStar.xsl -->
<xsql:include-xsql href="Emp.xsql" xmlns:xsql="urn:oracle-xsql"/>
Running EmpUsingRowStar.xsql from JDeveloper 3.1 we can see
the results shown in Figure
7-8.
Figure 7-8. Transformed
HTML output of EmpUsingRowStar.xsql
So our ROW/* template is correctly working not only for the
<EMPNO> and <ENAME> elements, but also for the additional
<SAL> and <DEPTNO> elements in the result. However, the results
look pretty plain and are missing column headers. Let's fix that.
Processing Source Nodes in Different
Modes
Today, many developers creating HTML pages use Cascading Style
Sheets (CSS) to separate the presentation style information for many pages
into a single, external stylesheet file, then reference the CSS stylesheet
from the HTML pages to control font and color information globally. We can
leverage this same tried and true technique in the HTML pages we create simply
by placing the correct <link> tag inside the <head> section of our
HTML page. If our CSS stylesheet is called Table.css then the <head> and
<link> elements we need look like this:
To create table column headers in a generic way, we need to
process all child elements of a <ROW> and then use the names of the
elements--as opposed to their values--as the content of the table header
cells. However, we already have a template with a ROW/* match pattern to
process the children of a <ROW>; we're using it to create the table
cells for each row generically.Specifically, we need a way to process the same
source tree elements multiple different ways to generate the column headers.
We need to process the children of a <ROW> as follows:
Once in a special "Column Headers" mode, to
transform the children of a <ROW> into the appropriate column
headers
Once in a regular way to format the query results
Luckily, XSLT has just the functionality we need. When you
create a template, in addition to the match pattern, it can also have a mode
attribute that assigns a name to the special mode in which you want to invoke
the template. Since we need a special mode to format column headers, we can
create a template with match="ROW/*" and mode="ColumnHeaders".
The name of the mode needs to be meaningful only to us; the processor never
interprets the name in any way. The template looks like this:
<!-- Match any element child of a ROW when in "ColumnHeaders" Mode -->
<xsl:template match="ROW/*" mode="ColumnHeaders">
<th>
<!-- Put the value of the *name* of the current element -->
<xsl:value-of select="name(.)"/>
</th>
</xsl:template>
Now, when we're processing a <ROW> element child in
ColumnHeaders mode, we create <th> table header elements instead of
<td> table cell elements, and we use the XPath name( ) function to refer
to the name of the current element instead of to its value. Remember that in
XPath, the dot represents the current node, so name(.) is the name of the
current node.
When you create templates with an associated mode, you have to
explicitly request that the engine process a list of nodes using that mode.
You accomplish this by going beyond the default use of <xsl:apply-templates>
(which, by default, processes the children of the current node without using
any special mode) to include a mode="CurrentHeaders" attribute, like
this:
<!-- Apply templates to children of the current node in "ColumnHeader" mode -->
<xsl:apply-templates mode="ColumnHeaders"/>
We need the column headers to be created before all of the
<ROW> elements are processed to produce the table rows, so we add the
above <xsl:apply-templates> inside our ROWSET template, like this:
<xsl:template match="ROWSET">
<table border="1" cellspacing="0">
<!-- Apply templates in "ColumnHeader" mode first -->
<xsl:apply-templates mode="ColumnHeaders"/>
<!-- Then apply templates to all child nodes normally -->
<xsl:apply-templates/>
</table>
</xsl:template>
However, if we attempt to use this template as is, the <xsl:apply-templates>
for the ColumnHeader mode will process all of the child nodes of the <ROWSET>,
since that's what <xsl:apply-templates> does. This will produce a set of
column headers across the top for each <ROW> in the <ROWSET>,
which will give us many repeated column headers. We need to process just a
single child <ROW> of the <ROWSET> to pick up the column header
names.
We can handle this easily by modifying the default behavior of
<xsl:apply-templates> by adding an optional select attribute that
specifies an XPath expression, identifying the list of nodes to process. We
accomplish this by changing:
Now we can select the list of child elements under only the
first <ROW> child of <ROWSET>. This will give us just a single set
of column headers.
There is no longer anything specific to the Emp.xml document
left in this stylesheet. It can handle the task of transforming any <ROWSET>
into an HTML table with column headers, so we'll name it appropriately. The
final TableBaseWithCSS.xsl stylesheet, incorporating CSS and column headers,
produced using modes, appears in Example
7-5.
Example 7-5:Transforming Any
ROWSET into a Table with Headers
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--
| TableBaseWithCSS:
| Basic stylesheet to format any ROWSET of ROWS into a table
| with column headings in a generic way. Leverages Table.css
| CSS stylesheet to control font/color information for the page.
+-->
<xsl:template match="/">
<html>
<!-- Generated HTML result will be linked to Table.css CSS stylesheet -->
<head><link rel="stylesheet" type="text/css" href="Table.css"/></head>
<body><xsl:apply-templates/></body>
</html>
</xsl:template>
<xsl:template match="ROWSET">
<table border="1" cellspacing="0">
<!-- Apply templates in "ColumnHeader" mode to just *first* ROW child -->
<xsl:apply-templates select="ROW[1]/*" mode="ColumnHeaders"/>
<!-- Then apply templates to all child nodes normally -->
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="ROW">
<tr><xsl:apply-templates/></tr>
</xsl:template>
<!-- Match any element child of a ROW -->
<xsl:template match="ROW/*">
<td><xsl:apply-templates/></td>
</xsl:template>
<!-- Match any element child of a ROW when in "ColumnHeaders" Mode-->
<xsl:template match="ROW/*" mode="ColumnHeaders">
<th>
<!-- Put the value of the *name* of the current element -->
<xsl:value-of select="name(.)"/>
</th>
</xsl:template>
</xsl:stylesheet>
If we create an XSQL page to test the stylesheet above, we see
that the result looks like Figure
7-9.
Figure 7-9. HTML table with
column headers
We've seen that by using multiple templates, it's possible to
build stylesheets that process source nodes in a more generic way, and that we
can use modes to process the same source tree nodes in different ways. Next,
we'll start to see how templates can be overridden to build on base libraries
of existing templates to create custom templates for new tasks.
Reusing and Customizing Existing
Stylesheets
Let's say we need to produce a table displaying employee
information where employees who earn more than $2000 are highlighted. This
task differs from our previous work in only one small detail: ROWs with a SAL
> 2000 need to be highlighted differently from other ROWs. We hope it's
possible to focus just on this new requirement. It most definitely is possible
with XSLT.
We can create a new EmpOver2000.xsl stylesheet that builds on
our TableBaseWithCSS.xsl stylesheet and adds one new template to handle the
new highlighting task. We can leverage our previous work by using the <xsl:import>
action at the top level of our stylesheet to import all of the templates we've
already created for doing the basic job of formatting a <ROWSET> as a
table. Example
7-6 shows the minimal syntax we need.
Example 7-6:Importing a Base
Stylesheet and Adding New Templates
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Import all the templates from "TableBaseWithCSS.xsl" as a base -->
<xsl:import href="TableBaseWithCSS.xsl"/>
<!-- Override imported template for ROW to match ROWs with a SAL > 2000 -->
<xsl:template match="ROW[ SAL > 2000 ]">
<tr class="Highlight"><xsl:apply-templates/></tr>
</xsl:template>
</xsl:stylesheet>
We've imported the TableBaseWithCSS.xsl stylesheet and added a
template with the match pattern of ROW[SAL>2000] to match nodes with a
<SAL> child element whose value is greater than 2000. Rather than
hard-coding font and color information directly into the template, notice that
we're using a CSS class attribute to refer to the name of a CSS class called
Highlight that will externally specify the fonts and colors to use for
highlighted rows. If we enhance our previous Table.css to include the new
Highlight CSS class like this:
Now, when we request this new EmpOver2000.xsql page, we see
what's shown in Figure
7-10.
Figure 7-10. HTML table
with high-paid employees highlighted
When processing the source tree using this stylesheet, for
each child <ROW> in the list of children of the <ROWSET> element,
the XSLT processor looks for templates that match <ROW>. Earlier, there
was only a single template with a match pattern of "ROW", so there
was only one template to choose from. However, in EmpOver2000.xsl the
match="ROW" template is imported from the TableBaseWithCSS.xsl
stylesheet, and we've also added a new match="ROW[SAL>2000]"
template. This means that when processing a <ROW> element in the current
node list for rows that have a <SAL> over 2000, the processor finds two
matching templates. Since the current node is a <ROW> element, it
matches the match="ROW" template, but since it's a <ROW> with
a SAL greater than 2000, it also matches the match="ROW[SAL>2000]"
template.
Remember from the basic transformation rules we learned
earlier in this chapter that the processor considers all matching templates
and then selects the one that matches best. In this case, the ROW[SAL>2000]
is a more specific pattern than the basic ROW pattern, so ROW[SAL>2000]
qualifies as a better match.
Let's try another example that imports TableBaseWithCSS.xsl
and:
Formats even-numbered rows in one color
Formats odd-numbered rows in a different color
Formats rows in the "Top-Secret" department
to say "Classified"
The stylesheet that accomplishes these tasks appears in Example
7-7.
Example 7-7:Formatting Alternating
Rows and Conditionally Hiding Data
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- Import all the templates from "TableBaseWithCSS.xsl" as a base -->
<xsl:import href="TableBaseWithCSS.xsl"/>
<!-- Match all ROWS in Top-Secret Department 20 -->
<xsl:template match="ROW[ DEPTNO = 20 ]">
<tr>
<td align="center" colspan="{count(*)}">
<table border="0">
<tr>
<td>Classified</td>
</tr>
</table>
</td>
</tr>
</xsl:template>
<!-- Match all even ROWS -->
<xsl:template match="ROW[ position( ) mod 2 = 0 ]">
<tr class="Even"><xsl:apply-templates/></tr>
</xsl:template>
<!-- Match all odd ROWS -->
<xsl:template match="ROW[ position( ) mod 2 = 1 ]">
<tr class="Odd"><xsl:apply-templates/></tr>
</xsl:template>
</xsl:stylesheet>
The stylesheet contains three additional templates that match:
Even rows with ROW[ position( ) mod 2 = 0 ]
Odd rows with ROW[ position( ) mod 2 = 1 ]
Top-Secret rows with ROW[ DEPTNO=20 ]
The stylesheet leverages the XPath position( ) function and
mod operator to calculate the remainder by integer division by two of the
current position in the current node list. Rows in even-numbered positions
will be divisible by two so they have a zero remainder. Rows in odd-numbered
positions have a remainder of one.
Applying this stylesheet produces the results shown in Figure
7-11. This does format the even and odd rows but--oops!--we've just
revealed our Top-Secret Department 20 information to users browsing the page.
Figure 7-11. HTML table
with alternating employee rows highlighted
This unexpected result occurs because of the way XSLT resolves
conflicts among multiple matching XPath expressions. For rows in Department
20, the XSLT processor considers all the templates that match the <ROW>
element in question. If the row is in an even position in the list, it will
match both ROW[position( ) mod 2 = 0] and ROW[DEPTNO=20]. Similarly, if it's
in an odd position in the list, it will match the ROW[position( ) mod 2 =0]
template and the ROW[DEPTNO=20] template. Unlike the previous example we
worked with (when it was clear to the processor that one template was more
specific than another), in this case, both templates match a specific
<ROW> element name and both templates have a qualifying predicate. Based
on the XSLT template conflict resolution rules, neither one is better. In this
situation, the processor picks the template that occurs last in the
stylesheet. ROW[DEPTNO=20] was never selected because it was at the top of the
stylesheet, above both the "even row" and "odd row"
templates.
Avoiding Template Conflicts with
Priorities
The basic scheme for determining which templates are more
specific than others is as follows: the generic pattern * is less specific
than a pattern like SOMETHING or xyz:SOMETHING, which is less specific than
SOMETHING[predicate] or SOMETHING/SOMETHINGELSE.
But when multiple patterns exist at the same level of
specificity, you have to help the XSLT processor by telling it explicitly
which templates are higher priority than others. You can assist the processor
in this tie-breaking task by assigning a priority="realnumber"
attribute on your template. The priority can be any positive or negative real
number. When no "best" template can be selected automatically by the
processor, the template with the highest assigned priority wins. A priority
greater than 0.5 makes your template more important than any of the built-in
priorities.
So, if we add a priority="2" attribute to our
ROW[DEPTNO=20] template, we make it more important than the even row and odd
row templates. When a row with DEPTNO equal to 20 is processed, the ROW[DEPTNO=20]
template will be chosen by the processor. Example
7-8 shows the stylesheet with the priority properly indicated.
Example 7-8:Getting the Right
Template to Fire by Indicating Priorities
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- Import all the templates from "TableBaseWithCSS.xsl" as a base -->
<xsl:import href="TableBaseWithCSS.xsl"/>
<!-- Match all ROWS in Top-Secret Department 20 -->
<xsl:template match="ROW[ DEPTNO = 20 ]" priority="2">
<tr>
<td align="center" colspan="{count(*)}">
<table border="0">
<tr>
<td>Classified</td>
</tr>
</table>
</td>
</tr>
</xsl:template>
<!-- Match all even ROWS -->
<xsl:template match="ROW[ position( ) mod 2 = 0 ]">
<tr class="Even"><xsl:apply-templates/></tr>
</xsl:template>
<!-- Match all odd ROWS -->
<xsl:template match="ROW[ position( ) mod 2 = 1 ]">
<tr class="Odd"><xsl:apply-templates/></tr>
</xsl:template>
</xsl:stylesheet>
Rerunning the example with this modified stylesheet shows us
that the result is now what we are expecting, as illustrated in Figure
7-12.
Figure 7-12. Template
priorities at work to produce correct output
Creating Reusable Named Templates
Next, we'll look at a simple example of formatting numbers to
make salaries appear as dollars and cents and we'll refine our strategy for
coloring the alternating rows. XSLT includes the format-number( ) function,
which allows any element whose value can be converted to a number to be
formatted using the number format masks specified by the
java.text.DecimalFormat class in the Java JDK. We see this function in action
in the following stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Import all the templates from "TableBaseWithCSS.xsl" as a base -->
<xsl:import href="TableBaseWithCSS.xsl"/>
<!-- Another technique for alternating row colors -->
<xsl:template match="ROW">
<!-- value of class attribute will alternate between "tr0" and "tr1" -->
<tr class="tr{position( ) mod 2}"><xsl:apply-templates/></tr>
</xsl:template>
<xsl:template match="ROW/SAL">
<td align="right">
<xsl:value-of select="format-number(.,'$0.00')"/>
</td>
</xsl:template>
</xsl:stylesheet>
Here we're again importing the TableBaseWithCSS.xsl and
including a template to accomplish alternating row coloring, as well as a
template matching ROW/SAL to override the formatting of <SAL> elements
that occur as children of <ROW> elements. Note that we're employing a
different technique to handle the alternating rows in this example. Rather
than using separate even row and odd row templates, we use a single
<ROW> template but alternate the name of the CSS class in use on the row
by using an attribute value template:
<tr class="tr{position( ) mod 2}"><xsl:apply-templates/></tr>
This constructs <tr> elements in the result that will
have a class attribute whose value will alternate between tr0 and tr1,
depending on whether we're in an even or odd row, respectively. We can add CSS
classes in our Table.css CSS stylesheet to define the colors we want, like
this:
we can request the page and see the results shown in Figure
7-13.
Figure 7-13. HTML table
showing employees with formatted salaries
If we anticipate frequently needing to format table cells with
numeric values, we can further factor our templates for reuse by creating a
named template to handle the formatting. We can replace the match attribute on
the <xsl:template> element with a name attribute to turn the template
into a callable subroutine to be used in any situation requiring a table cell
to be formatted as dollars and cents, like this:
<!-- "Utility" template to format money is a common way -->
<xsl:template name="moneyCell">
<td align="right"><xsl:value-of select="format-number(.,'$0.00')"/></td>
</xsl:template>
Then, wherever we need to invoke the template, we can use <xsl:call-template>
to invoke the subroutine by name with the syntax:
<xsl:call-template name="moneyCell"/>
Named templates are never automatically triggered by the
processor; they must be explicitly invoked. When a calling template invokes a
named template using <xsl:call-template>, the literal elements and xsl
actions in the named template are instantiated as if they had been included at
the current position in the invoking template. This means that the called
template sees the same current node as the template that invoked it, in
contrast to <xsl:apply-templates select="pattern "/>, which
changes the current node list.
Common Template Errors
As you begin using match-pattern-based
templates and named templates together in your XSLT work, a very
common mistake is to accidentally type:
<!-- My template to match ROWSET/ROW
incorrectly -->
<xsl:template name="ROWSET/ROW">
when you mean:
<!-- My template to match ROWSET/ROW -->
<xsl:template match="ROWSET/ROW">
If it seems that your templates are not being
triggered properly, these common errors are typically the first
thing you should check. To help further diagnose the problem, you
can add an <xsl:message> element at any point in a template to
emit a helpful debugging message. Anything that is legal in a
template can be used as the content of <xsl:message>.
By placing a strategic <xsl:message>
element in your template and using the oraxsl command-line utility
to perform the transformation, you will see all messages on the
console. If you use the optional -debug flag when invoking oraxsl,
you will see helpful line number information for each message as
well.
You can use <xsl:message> to emit a
simple string argument like this:
<xsl:message>Got Here!</xsl:message>
or the value of any XPath expression,
including any variable values:
Like other templates, named templates can be included in a
"library" stylesheet that is destined to be imported when their
functionality is needed; for example:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Handle even/odd formatting of rows using CSS classes "tr0" and "tr1" -->
<xsl:template match="ROW">
<tr class="tr{position( ) mod 2}"><xsl:apply-templates/></tr>
</xsl:template>
<!-- "Utility" template to format money is a common way -->
<xsl:template name="moneyCell">
<td align="right"><xsl:value-of select="format-number(.,'$0.00')"/></td>
</xsl:template>
</xsl:stylesheet>
We can include both our alternating row coloring and our named
moneyCell template in this CommonLibrary.xsl stylesheet and then import it
into the following FormatSalUsingLibrary.xsl stylesheet:
Notice that the ROW/SAL template here uses an <xsl:call-template>
to invoke the common moneyCell template's services by name. The result is the
same as that produced by our earlier stylesheet, but now we're reusing common
templates from two different libraries, including both normal pattern-matching
templates and named templates.
At this point, we've got our XSLT feet firmly planted on the
ground and we're ready to delve deeper into using XSLT in combination with
Oracle XSQL Pages in the next chapter. Then in Chapter 9, XSLT Beyond the
Basics, we cover a number of important XSLT topics beyond the basics we've
learned here, including using XSLT variables, efficient sorting and grouping
of information, and a number of basic transformation techniques for XML
datagrams. We'll see in the many examples throughout the rest of this book
that by using XSQL Pages together with stylesheets, we can publish any data in
any format we need for the Web.