Chapter 6 - We Want Results! | |
XSLT For Dummies | |
by Richard Wagner | |
Hungry Minds 2002 |
XSLT is all about moving information from one place to another. Within all this hustle and bustle, the element is at the center of this activity. After all, most everything you move into the new document is an element or comes from an element in some way. When you want to perform a simple copy of an elements content or tags or even copy everything from one tree to another, use the techniques I discuss in the following sections. Content onlyIn previous chapters, you find out that xsl:apply-templates , by calling the elements built-in templates, is used to push an elements content to the result tree, stripping away its tags in the process. I can use this technique to output just the content of the price element in the coffee.xml document by using the following stylesheet: <!-- coffee-copy_elementcontent.xsl --> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <!-- Add just content --> <xsl:template match="price"><xsl:text> </xsl:text> <xsl:apply-templates/> </xsl:template> <!-- Show just prices --> <xsl:template match="coffees"> <xsl:apply-templates select="region/coffee/price"/> </xsl:template> </xsl:stylesheet> The purpose of the first template rule is fairly simple; it outputs the price e lements content. Using the xsl:text instruction, a line break is added before each line to separate the numbers that are generated in the transformation. Although this template rule can produce the results Im looking for, I have to keep in mind the context of the price elementit is a child of the coffee element, which is a child of region , which is a child of coffees . As a result, if I only use the price template rule in the stylesheet, I am going to get additional content in my result document that Im not expecting. In case youre wondering why, remember the built-in template rules (which I discuss in Chapter 4) that are run on each transformationan xsl:apply-templates is run by default on any element that doesnt have an explicitly defined template rule. To prevent built-in templates from kicking in on the other elements, a second template rule is added that uses the coffees document element as the match pattern. However, I cant just create an empty template rule ( <xsl:element match="coffees"/> ) because it would override the built-in template rules not only for coffees but also its descendants as well. In essence, it would suffocate the price template rule I just defined. So, in this particular case, the easiest solution is to make sure that the xsl:apply-templates instruction is applied only to the price element by using region/coffee/price as the select attribute value. The result that is generated is shown here: 11.99 12.99 14.99 9.99 Tags onlyThe xsl:copy instruction is one of two copy routines that XSLT has in its arsenal. I always consider it the decaffeinated version of the two, because it doesnt copy children or attributes and, by default, doesnt copy content either. Suppose you need to copy only the tags of a given element. If so, xsl:copy is a good candidate for the job. In the following code, my goal is to do the inverse of the preceding example: Output just the tags, not the content. To do so, I create a stylesheet that looks like the following: <!-- coffee-copy_elementcontent.xsl --> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <!-- Copy just tag --> <xsl:template match="price"> <xsl:copy/> </xsl:template> <!-- Add parent, run on just price --> <xsl:template match="coffees"> <prices> <xsl:apply-templates select="region/coffee/price"/> </prices> </xsl:template> </xsl:stylesheet> The price template rule contains an empty xsl:copy instruction to tell the XSLT processor to copy just the tags. Also, because I dont want any other parts of the coffee.xml document carried over, I use a similar template rule for coffees that I used in the previous example. However, because my result document is XML this time, I am going to add a prices document element into the result tree by adding literal text strings both before and after the xsl:apply-templates instruction. When an XSLT processor runs this stylesheet, the results generated are: <?xml version="1.0" encoding="utf-8"?> <prices> <price/> <price/> <price/> <price/> </prices> Copy allIf xsl:copy is the decaffeinated side of XSLT copying, xsl:copy-of must be like a venti- sized coffee at Starbucks: It packs a wallop! xsl:copy-of is surely the tool of choice when you want to copy the entire element and its various pieces. To copy both the tags and content of the price element, I can use the following XSLT code: <!-- coffee-copy_elementfull.xsl --> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <!-- Copy price element --> <xsl:template match="price"> <xsl:copy-of select="."/> </xsl:template> <!-- Add parent, run on just price --> <xsl:template match="coffees"> <prices> <xsl:apply-templates select="region/coffee/price"/> </prices> </xsl:template> </xsl:stylesheet> The XML output produced is shown here: <?xml version="1.0" encoding="utf-8"?> <prices> <price>11.99</price> <price>12.99</price> <price>14.99</price> <price>9.99</price> </prices> A second way to copy an element is through a method that can be called reconstruction. The gist of this method is that you use a combination of literal text and xsl:value instructions to reconstruct the pieces of the element. As shown in the following XSLT stylesheet, the price template rule uses the reconstruction technique to achieve identical results as the preceding xsl:copy-of example: <!-- coffee-copy_elementrecreate.xsl --> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <!-- Recreate element --> <xsl:template match="price"> <price><xsl:value-of select="."/></price> </xsl:template> <!-- Add parent, run on just price --> <xsl:template match="coffees"> <prices> <xsl:apply-templates select="region/coffee/price"/> </prices> </xsl:template> </xsl:stylesheet>
|