Building Oracle XML ApplicationsBy Steve MuenchSeptember 2000 1-56592-691-9, Order Number: 6919 810 pages, $44.95, Includes CD-ROM |
Chapter 7
In this chapter:
Transforming XML with XSLT
XSLT Processing Mechanics
Single-Template Stylesheets
Understanding Input and Output Options
Improving Flexibility with Multiple TemplatesWe'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.
Consider a simple XML document like this:
<!-- Emp.xml -->
<ROWSET>
<ROW num="1">
<EMPNO>7839</EMPNO>
<ENAME>KING</ENAME>
</ROW>
<ROW num="2">
<EMPNO>7788</EMPNO>
<ENAME>SCOTT</ENAME>
</ROW>
</ROWSET>
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 like7788
orSCOTT
. 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:
<xsl:template match="ROWSET/ROW[ENAME]">
<Employee id="NX-{EMPNO}">
<xsl:value-of select="ENAME"/>
</Employee>
</xsl:template>
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 valueNX-{EMPNO}
is replaced by the value of theEMPNO
XPath expression. This evaluates to7839
, so the final value for theid
attribute in the result tree isNX-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 thexsl
prefix since it will not recognize them as XSLT actions.Consider the stylesheet in Example 7-1.
Example 7-1: Single-Root Template Stylesheet to Transform Emp.xml to HTML
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:for-each select="ROWSET">
<table border="1" cellspacing="0">
<xsl:for-each select="ROW">
<tr>
<td><xsl:value-of select="EMPNO"/></td>
<td><xsl:value-of select="ENAME"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
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
<html>
<body>
<table border="1" cellspacing="0">
<tr>
<td>7839</td>
<td>KING</td>
</tr>
<tr>
<td>7788</td>
<td>SCOTT</td>
</tr>
</table>
</body>
</html>
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:<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<body>
<xsl:for-each select="ROWSET">
<table border="1" cellspacing="0">
<xsl:for-each select="ROW">
<tr>
<td><xsl:value-of select="EMPNO"/></td>
<td><xsl:value-of select="ENAME"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:for-each>
</body>
</html>
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 oftext/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.
Consider the following example source document:
<!-- King.xml -->
<ROWSET>
<ROW>
<EMPNO>7839</EMPNO>
<ENAME>KING</ENAME>
</ROW>
</ROWSET>
The following stylesheet uses the
xml
output method to transform this King.xml <ROWSET> document into an <Invitation> document:<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Invitation>
<To>
<xsl:value-of select="ROWSET/ROW/ENAME"/>
<xsl:text> & Family</xsl:text>
</To>
</Invitation>
</xsl:template>
</xsl:stylesheet>
Transforming King.xml using this stylesheet produces the following result:
<?xml version="1.0"?>
<Invitation>
<To>KING & Family</To>
</Invitation>
TIP:
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:<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<body>
<p>
<xsl:value-of select="ROWSET/ROW/ENAME"/>
<xsl:text> & Family</xsl:text>
</p>
<img src="images/{ROWSET/ROW/EMPNO}.gif"/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Transforming King.xml using this stylesheet produces the following result:
<html>
<body>
<p>KING & Family</p>
<img src="images/7839.gif">
</body>
</html>
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.
Table 7-1: Common xsl:output Settings If target document needs
Set this <xsl:output> attribute
Raw text output
method="text"
HTML output
method="html"
XML output
method="xml"
Character set encoding ENC
encoding="
ENC"
SYSTEM Identifier URI
doctype-system="
URI"
PUBLIC Identifier URI
doctype-public="
URI"
Beautifying whitespace added
indent="yes"
No additional whitespace added
indent="no"
MIME/Media Type of TYP
media-type="
TYP"
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 singlemain( )
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
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="no"/>
<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>
<xsl:template match="EMPNO">
<td><xsl:apply-templates/></td>
</xsl:template>
<xsl:template match="ENAME">
<td><xsl:apply-templates/></td>
</xsl:template>
</xsl:stylesheet>
The way to read a template that looks like this:
<xsl:template match="ROWSET">
<table border="1" cellspacing="0"><xsl:apply-templates/></table>
</xsl:template>
is as follows:
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. Ourmatch="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
, andSCOTT
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:
<xsl:template match="/|*">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text( )|@*">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="processing-instruction( )|comment( )"/>
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 commentNothing
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 itsmatch
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
, andSCOTT
, the built-in rule fortext( )
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:
<xsl:template match="EMPNO">
<td><xsl:apply-templates/></td>
</xsl:template>
<xsl:template match="ENAME">
<td><xsl:apply-templates/></td>
</xsl:template>
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:oraxsl Emp.xml EmpUsingRowStar.xsl
We get the output:
<html>
<body>
<table border="1" cellspacing="0">
<tr>
<td>7839</td>
<td>KING</td>
</tr>
<tr>
<td>7788</td>
<td>SCOTT</td>
</tr>
</table>
</body>
</html>
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 analogouselements
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:<head><link rel="stylesheet" type="text/css" href="Table.css"/></head>
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 aROW/*
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 withmatch="ROW/*"
andmode="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 inColumnHeaders
mode, we create <th> table header elements instead of <td> table cell elements, and we use the XPathname( )
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:<xsl:apply-templates mode="ColumnHeaders"/>
to:
<xsl:apply-templates select="ROW[1]/*" mode="ColumnHeaders"/>
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:
ROW
s with aSAL
>
2000
need to be highlighted differently from otherROW
s. 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 CSSclass
attribute to refer to the name of a CSS class calledHighlight
that will externally specify the fonts and colors to use for highlighted rows. If we enhance our previous Table.css to include the newHighlight
CSS class like this:body { font-family: Verdana }
th { background-color: yellow }
.Highlight { background-color: #e7e7e7 }
then all that's left to do is to build an XSQL page to include our original Emp.xsql information and transform it using EmpOver2000.xsl like this:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="EmpOver2000.xsl"?>
<xsql:include-xsql href="Emp.xsql" xmlns:xsql="urn:oracle-xsql"/>
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 thematch="ROW"
template is imported from the TableBaseWithCSS.xsl stylesheet, and we've also added a newmatch="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 thematch="ROW"
template, but since it's a<ROW>
with a SAL greater than 2000, it also matches thematch="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 basicROW
pattern, soROW[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 andmod
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 bothROW[position( ) mod
2
=
0]
andROW[DEPTNO=20]
. Similarly, if it's in an odd position in the list, it will match theROW[position(
)
mod
2
=0]
template and theROW[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 than0.5
makes your template more important than any of the built-in priorities.So, if we add a
priority="2"
attribute to ourROW[DEPTNO=20]
template, we make it more important than the even row and odd row templates. When a row withDEPTNO
equal to20
is processed, theROW[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 thejava.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
andtr1
, 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:body { font-family: Verdana; font-size: 8pt }
th { background-color: yellow }
.Highlight { background-color: #e7e7e7 }
.tr1 {background-color: #f7f7e7; color: black}
.tr0 {background-color: #f9f9f9; color: black}
By building a quickie XSQL page to include Emp.xsql and format it with this new stylesheet, as follows:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="FormatSal.xsl"?>
<xsql:include-xsql href="Emp.xsql" xmlns:xsql="urn:oracle-xsql"/>
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 aname
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 andxsl
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.
when you mean:
<!-- My template to match ROWSET/ROW -->
<xsl:template match="ROWSET/ROW">Another related mistake is to accidentally type:
<!-- Apply templates matching ROWSET/ROW incorrectly-->
<xsl:apply-templates match="ROWSET/ROW">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:<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="TableBaseWithCSS.xsl"/>
<xsl:import href="CommonLibrary.xsl"/>
<xsl:template match="ROW/SAL">
<xsl:call-template name="moneyCell"/>
</xsl:template>
</xsl:stylesheet>
Notice that the
ROW/SAL
template here uses an <xsl:call-template> to invoke the commonmoneyCell
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.
Back to: Building Oracle XML Applications
© 2001, O'Reilly
& Associates, Inc.
webmaster@oreilly.com