XML and SQL Server 2000
Templates consist of a pattern match to the source tree, a set of rules to apply to generate the result tree, and possibly data to transfer to the result tree. A template is employed by a source element to create part of the result tree. Construction of the result tree starts by finding the template rule for the root node and employing its template. A template also can contain elements from the XSLT namespace that are instructions for creating result tree fragments or parts of the result tree. When a template is employed, each instruction in the template is executed and replaced by the result tree fragment it creates. A template can contain elements that specify literal result element structure. A literal result element is literal text within a template that is transferred to the output tree as is (literally). An example of this that you'll see many times in this book is a template that has the literal text element <HTML> , which is the starting tag for an HTML document. When a source tree node match is found for the template that contains this literal element, the <HTML> tag will be transferred to the result tree, and an HTML document will be started.We'll see examples very shortly. Let's begin with the first part of a template, a discussion of patterns and how we match them. It's impossible to generate a result tree that means anything if we incorrectly identify the element we want to modify. Patterns (Abbreviated Syntax)
This section is called "Abbreviated Syntax" for a reason. Remember that the XPath specification, among other important things, defines how we navigate around an XML document using location paths.Well, Microsoft does not support the entire XPath specification, and rather than waste your time giving you the entire specification, I'll explain what is called the abbreviated syntax. When we get to the point where the supported syntax will be necessary to understand what we're doing, I'll explain in detail what Microsoft supports and what it doesn't. As shown in the example I gave previously, xsl:template uses the "match=" attribute to determine whether it should apply its rules to the current context node. A pattern specifies a set of conditions on a node. A node that satisfies the conditions matches the pattern; a node that does not satisfy the conditions does not match the pattern. The abbreviated form of pattern matching is, as I am sure you'll notice, uncannily similar to the method UNIX and DOS use to navigate their directory structure. Hopefully, this will make it simpler for you to learn the syntax. Table 2.6 contains the symbols used for pattern matching and describes how they are employed. Extensive examples will be given in the next section. Table 2.6. Symbols Used for Pattern Matching
Pattern Examples
So, armed with the definitions listed in Table 2.6, we should be able to identify just about any node of an XML document. Let's take our original XML document from Chapter 1, given again in Listing 2.5, and look at some examples to make sure we know how these work.The examples are given in Table 2.7. Listing 2.5 Sample XML Document
<!-- ******* Resumes for Potential Hires ******* --> <RESUMES xmlns='http://www.myorg.net/tags'> <PERSON PERSONID="p1"> <NAME> <LAST>Shelton</LAST> <FIRST>Rick</FIRST> </NAME> <ADDRESS> <STREET>911 Intranet Ave.</STREET> <CITY>Canberra</CITY> <COUNTRY>Australia</COUNTRY> <PC>A34G-90</PC </ADDRESS> <TEL>(+612) 111-2345</TEL> <EMAIL>shelton@somewhere.com</EMAIL> </PERSON> <PERSON PERSONID="p2"> <NAME> <LAST>Tenney</LAST <FIRST>Corey</FIRST> </NAME> <ADDRESS> <STREET>211 Yardwork Circle</STREET> <CITY>Roy, UT</CITY> <COUNTRY>USA</COUNTRY> <ZIP>64067</ZIP> </ADDRESS> <TEL/> <EMAIL>tenney@yardwork.com</EMAIL> </PERSON> </RESUMES> Table 2.7. XPath Pattern Examples
Template Rules
Listing 2.6 gives the template tag definition. Listing 2.6 Template Tag Syntax
<!-- Category: top-level-element --> <xsl:template match = pattern name = name priority = number mode = name> <!-- Content: (instructions, data) --> </xsl:template> When a stylesheet is applied to an XML source document for processing, the first thing the processor does is convert these documents into a stylesheet tree and a source tree, respectively, in memory. After this is accomplished, the transformation process can begin. Root Node "/"
The first order of business for the transformation process is to find the root node of the source document, like this: <xsl:template match="/"> ... </xsl:template> There should be a template like the previous one that locates this root node. If there is more than one template that could be a candidate for processing the root, there is an established order of precedence so that only one will perform the processing.
Applying Template Rules
Let's look a little deeper into what exactly is contained within this template. Take a look at the comment node in the template definition given in Listing 2.6. It states that there are instructions and data and that's all. We've seen this in a previous discussion in this chapter. The instructions in the template are carried out one by one, and the data is transferred to the result tree as it is encountered in the processing. The data contained in the template is referred to as literal result elements, as shown by Listing 2.7. Listing 2.7 Literal Result Elements
<xsl:template match="/"> <xsl:comment>My first template</xsl:comment> <HTML> ... </HTML> This is just text </xsl:template> This template consists of an instruction ( xsl:comment ), some literal result elements (the two HTML elements), and text. When this template is employed, the instructions will be executed according to their rules, the literal result elements will be copied as element nodes, and the text will be copied as a text node. Let's take our template example from Listing 2.7 a little further. Look at Listing 2.8. Listing 2.8 Apply Templates with Literal Result Elements
<xsl:template match="/"> <xsl:comment>My first template</xsl:comment> <HTML> <BODY> <xsl:apply-templates/> </BODY </HTML> This is just text </xsl:template> In addition to the instruction and literal result elements we just discussed, we now have two additional literal result elements (the two BODY tags), but we also have something new here: an XSL instruction! This instruction is of critical importance when written as it is here. This instruction says, "Select all child nodes of the current element and employ the matching template rule for each of them if any exist." What does all this mean? Let's say our XML document is well formed and therefore has a single document element <resumes> . Sound familiar? Our processor will begin to search our stylesheet and try to find a template that matches <resumes> , like this one: <xsl:template match="resumes" Listing 2.9 gives the <xsl:apply-templates> definition. If and when it finds a matching template, it will carry out the same procedures that our previous template did. It will check for multiple template matches and use the precedence rules to determine the correct one to use. If no template is found, the appropriate built-in template will be called. The matching template is processed. By the way, if the node being processed has no child nodes, <xsl:apply-templates/> doesn't do anything. Listing 2.9 <xsl:apply-templates> Definition
<!-- Category: instruction --> <xsl:apply-templates select = node-set-expression mode = qname> <!-- Content: (xsl:sort xsl:with-param)* --> </xsl:apply-templates> The select attribute is used to process nodes, specifically selected nodes instead of processing every child node. The value of the select attribute is an XPath expression, which must evaluate to a set of nodes. The selected set of nodes is processed in document order unless you've specified a sort order. We'll talk about sorting shortly. The following example processes all of the zipcode or postal code children of the address node: <xsl:template match="address"> <xsl:apply-templates select="pczip"> </xsl:template> The following example processes all of the first elements of the name elements that are children of person: <xsl:template match="person"> <fo:inline-sequence> <xsl:apply-templates select="name/first"/> </fo:inline-sequence> </xsl:template> This example processes all of the street descendant elements of the resumes element. <xsl:template match="resumes"> <xsl:apply-templates select=".//street"/> </xsl:template> Hopefully, by now you've made the connection between these template matches and how you move the context node around. <xsl:value-of>
The <xsl:value-of> element creates a text node in the result tree. The select attribute, which is mandatory, is an XPath expression identical to the expressions used in the "select=" attribute of apply-templates . This XPath expression is evaluated, and the resulting object is converted to a string, which specifies the string value of the created text node. If the string is empty, no text node will be created. Examine the following code: <!-- Category: instruction --> <xsl:value-of select = string-expression disable-output-escaping = "yes" "no" /> For example, Listing 2.10 creates an HTML paragraph from a last child of a name element. The paragraph will contain the test "Last name is: " followed by the value of the last element, which is the current node. Listing 2.10 Demonstrating the value-of Clause
<xsl:template match="name/last"> <p> <xsl:text>Last name is : </xsl:text> <xsl:value-of select="last"/> </p> </xsl:template>
Listing 2.11 accomplishes the exact same thing. Notice the different select expression. Listing 2.11 Specifying the "." to Select the Current Element
<xsl:template match="name/last"> <p> <xsl:text>Last name is : </xsl:text> <xsl:value-of select="."/> </p> </xsl:template> In one last example here, Listing 2.12 creates an HTML paragraph from a person element with first and last children elements.The paragraph will contain the string value of the first child element of the current node followed by a space and the string value of the last child element of the current node. Listing 2.12 Adding Whitespace
<xsl:template match="person"> <p> <xsl:value-of select="given-name"/> <xsl:text> </xsl:text> <xsl:value-of select="family-name"/> </p> </xsl:template> Default Templates
If no matching template can be found for a <xsl:apply-templates match= > instruction, the built-in template rules are used. Table 2.8 illustrates what the built-in template rules do for each type of node. Table 2.8. Default Rules for Template Matches
There is no pattern that can match a namespace node, so the built-in template rule is the only template rule applied for namespace nodes. Also, the built-in template rules have lower import precedence than any other template rule. Thus, they can be overridden by including an explicit template rule. Named Templates
You can directly employ a template by name using the xsl:call-template element, which has a required name attribute that identifies the template to be employed. An xsl:template element that has a defined name attribute specifies a named template, as illustrated by the following example: <!-- Category: instruction --> <xsl:call-template name = qname> <!-- Content: xsl:with-param* --> </xsl:call-template> The value of the name attribute is a QName . A QName is an XML name that optionally has a namespace prefix attached. An example of a nonprefixed name is resume or person . An example of a prefixed name is xsl:address or xsl:street . If the QName has a prefix, it must match a namespace that is in scope at the location the name is used. The match , mode , and priority attributes on an xsl:template element do not affect whether the template is invoked by an xsl:call-template element. Similarly, the name attribute on an xsl:template element does not affect whether the template is invoked by an xsl:apply-templates element. Here are some important points to remember about xsl:template:
Creating the Tree
Up to this point, we've taken what's in the source tree and the stylesheet tree and created a result tree that is basically a rigid copy of what's in the source tree. This doesn't do much for flexibility. What if we have to create brand-new elements in the result tree based on what we find in the source tree? How about taking an element in the source tree and converting it to an attribute of a result tree element or taking an attribute of the source tree and making it an element in the result tree? XSLT elements such as xsl:element , xsl:attribute , and xsl:text , when used in conjunction with attribute templates, can accomplish these conversions. In essence, with these new XSLT elements, we can create, delete, or modify XML elements and attributes on the fly. The following sections discuss these elements. Attribute Templates
Let's say we want to change the name element in our original resumes document from having the children elements of first and last to having the attributes of first and last. We want to change <NAME> <LAST></LAST> <FIRST></FIRST> </NAME> to <NAME LAST="" FIRST="" /> How can we do this? One thing we definitely can't do is this: <xsl:template match="NAME"> <NAME LAST="<xsl:value-of select='LAST'/>" FIRST="<xsl:value-of select='FIRST'/>" /> Why not? The < character is not allowed inside attribute values. So what do we do? We create an attribute value template. In an attribute value that is interpreted as an attribute value template, an XPath expression can be used by surrounding the XPath expression with curly braces ({}). The expression, together with its surrounding curly braces, will be replaced by the result of evaluating the expression and converting the resulting object to a string. Listing 2.13 gives the partial template employed to accomplish what we want. Listing 2.13 XSLT Template Utilizing Attribute Value Templates
<xsl:template match="NAME"> <NAME LAST="{LAST}' FIRST="{FIRST}" /> /> The applicable fragment of the source tree originally was as follows : <PERSON PERSONID="p1"> <NAME> <LAST>Shelton</LAST> <FIRST>Rick</FIRST> </NAME> After applying the template in Listing 2.13, the result tree fragment will be as follows: <PERSON PERSONID="p1"> <NAME LAST="Shelton" FIRST="Rick" Creating New Elements
Now let's create new elements. The xsl:element element creates new elements in the result tree. The name of a new element to be created is specified by a required name attribute and an optional namespace attribute. The name attribute will be an attribute value template, which we learned about in the previous section. The xsl:element element's content is a template for the attributes and children of the element created in the result tree, as illustrated in Listing 2.14. Listing 2.14 Syntax for the <xsl:element > Element
<!-- Category: instruction --> <xsl:element name = { qname } namespace = { uri-reference } <!-- Content: template --> </xsl:element> Again, the best way of teaching is by example, so first we take our resumes file and add an element to it. Suppose we have our last example, in which the name element has the two attributes of first and last . We want to change the two attributes to new elements named FIRST and LAST . These two new elements will be children of the name element. Here's our source tree: <PERSON PERSONID="p1"> <NAME LAST="{LAST}" FIRST="{FIRST}" /> Listing 2.15 shows the template used to create the new elements. Listing 2.15 The Template that Creates New Elements
<xsl:template match="NAME"> <NAME> <xsl:element name={@LAST} <xsl:value-of select="LAST"> </xsl:element> <xsl:element name={@FIRST} <xsl:value-of select="FIRST"> </xsl:element> </NAME> /> Here's our result tree: <PERSON PERSONID="p1"> <NAME> <LAST>Shelton</LAST> <FIRST>Rick</FIRST> </NAME> Creating New Attributes
In the same way that we created new elements in the preceding section, we can create new attributes. The name of a new element to be created is specified by a required name attribute and an optional namespace attribute. The name attribute will be an attribute value template. An xsl:attribute element adds an attribute node to the element node in which it is contained. The content of the xsl:attribute element is a template for the value of the created attribute, as illustrated in Listing 2.16. Listing 2.16 Syntax for the <xsl:attribute > Element
<!-- Category: instruction --> <xsl:attribute name = { qname } namespace = { uri-reference }> <!-- Content: template --> </xsl:attribute> Again, let's go to examples. We'll take the name element of our document and make the email element (depending on what's there) a new attribute. Listing 2.17 is our source tree fragment, and Listing 2.18 is our template. Listing 2.17 Source Tree Element Used in Listing 2.18
<NAME> <LAST>Shelton</LAST> <FIRST>Rick</FIRST> </NAME> <ADDRESS> <STREET>911 Intranet Ave.</STREET> <CITY>Canberra</CITY> <COUNTRY>Australia</COUNTRY> <PC>A34G-90</PC </ADDRESS> <TEL>(+612) 111-2345</TEL> <EMAIL>shelton@somewhere.com</EMAIL> Listing 2.18 Template File that Makes the email Element an Attribute
<xsl:template match="NAME"> <NAME> <xsl:attribute name={EMAIL} <xsl:value-of select="EMAIL"> </xsl:element> </NAME> /> Here's the result tree fragment: <NAME EMAIL="shelton@somewhere.com"> <LAST>Shelton</LAST> <FIRST>Rick</FIRST> </NAME> Copying Nodes
The xsl:copy element provides an easy way to copy the current node and its namespace nodes. The attributes and children of the node are not automatically copied, though. This is what gives xsl:copy its power. You can specify exactly what you want copied and what you don't want copied, as illustrated in the following example: <!-- Category: instruction --> <xsl:copy use-attribute-sets = qnames > <!-- Content: template --> </xsl:copy> The following example shows how I would generate a result tree that contained only the elements of the source tree. All comments, processing instructions, text, and attributes would be filtered out as shown. <xsl:template match="*"> <xsl:copy> <xsl:apply-templates select="*"/> </xsl:copy> </xsl:template> The result of this would be as in Listing 2.19. Listing 2.19 Filtering Out Everything but the Elements
<RESUMES> <PERSON> <NAME> <LAST></LAST> <FIRST></FIRST> </NAME> ETC. I didn't tell the template to copy text nodes, so no element content was copied. If I wanted the content, the match and select expressions should be "* text()" . Creating Text Nodes
A template also can contain text nodes. Each text will create a text node with the same string value in the result tree. This enables the stylesheet author to replace whole elements with text, for example, if need be. This is illustrated in the following example: <!-- Category: instruction --> <xsl:text disable-output-escaping = "yes" "no"> <!-- Content: #PCDATA --> </xsl:text> One reason to use xsl:text elements is to insert whitespace in the result tree. If we were to combine the person's first and last name into a single element, we could insert a space character between them, as in Listing 2.20. Listing 2.20 Inserting Whitespace
<xsl:template match="NAME"> <NAME> <xsl:value-of select="FIRST"> <xsl:text> </xsl:text> <!--pass a single space character --> <xsl:value-of select="LAST"> </NAME> /> It is your responsibility as the stylesheet author to explicitly generate any spaces that are needed in the result. Remember the entities that we talked about when we were discussing DTDs? I didn't think so. Here's a list of them in Table 2.9. Table 2.9. Entities
The problem with these entities is that text is processed at the tree level, so putting markup of < in a template will be represented in the stylesheet tree by a text node that includes the character < . This will create a text node in the result tree that contains a < character, which will be represented by the markup < (or an equivalent character reference) when the result tree is output as an XML document. To prevent this from happening, set the disable-output-escaping of the xsl:text element to "yes" , as shown: <xsl:text disable-output-escaping="yes"> <NAME< <!--another way to create a NAME element --> </xsl:text> The output document will contain <NAME> . <xsl:comment>
The <xsl:comment> element creates a comment node in the result tree. The content of the <xsl:comment> element is a template for the string value of the comment node. The following example illustrates the syntax: <!-- Category: instruction --> <xsl:comment> <!-- Content: template --> </xsl:comment> For example, <xsl:comment> This file was generated via an XSL Transformation. </xsl:comment> This would create the following comment: <!-- This file was generated via an XSL Transformation.--> <xsl:processing-instruction>
The <xsl:processing-instruction> element creates a processing instruction node. The <xsl:processing-instruction> content is a template for the string value of the processing instruction node that is generated as shown: <!-- Category: instruction --> <xsl:processing-instruction name = { ncname }> <!-- Content: template --> </xsl:processing-instruction> The <xsl:processing-instruction> element has a required name attribute that specifies the name of the processing instruction node, as shown in the following example. The value of the name attribute is interpreted as an attribute value template. The following code generates the processing instruction: <xsl:processing-instruction name="xml-stylesheet"> href="resumes.css" type="text/css" </xsl:processing-instruction> |