Chapter 12

Combining XSLT Stylesheets

In This Chapter

bullet Including and importing stylesheets into another stylesheet

bullet Highlighting the similarities and differences of xsl:include and xsl:import

bullet Finding out about how xsl:include and xsl:import handle conflicts

bullet Using xsl:import to bring in external elements

A nyone who rents videos from a local video store has probably seen that little sticker that says, “Be kind; please rewind.” As you begin to work with a growing number of XSLT stylesheets, my guess is that you’ll quickly find that your motto is “Be kind, please combine.”

Managing XSLT code can be challenging, especially when you have common template rules or other instructions that you want to use in more than one context. Isolating sections of XSLT code and placing them into different files can be useful. This practice allows you to mix and match various stylesheets, depending on your current need, without resorting to copying and pasting code from one stylesheet to another.

Combining stylesheets can be a useful organizing tool for XSLT developers. In this chapter, you find out how you can combine XSLT stylesheets and call templates and other instructions from one stylesheet into another.

Comparing xsl:include and xsl:import

XSLT offers two closely related top-level elements for incorporating one stylesheet into another. These are:

bullet xsl:include is used to draw the top-level elements of the specified stylesheet into the calling stylesheet. The XSLT processor treats the incoming contents as if you literally inserted them at the point of the xsl:include element. Its required attribute is href, which specifies the file to be included:

<xsl:include href=”util.xsl”/> 

bullet xsl:import loads the specified stylesheet, but makes it something like an auxiliary resource, extending the top-level elements of the current stylesheet. It has the following syntax:

<xsl:import href=”util.xsl”/> 

Both of these elements sound pretty similar, don’t they? At first glance, the differences between xsl:include and xsl:import seem nominal, almost esoteric. But, on closer inspection, you notice some important differences in their usage and behavior. In the following sections, I discuss some of their similarities and differences.

Tip

When the XSLT processor encounters an xsl:include or xsl:import element, the processor takes all the contents of the referenced stylesheet (all the code between the xsl:stylesheet’s start and end tags) and processes them based on the rules of the import/include element.

Remember

You use xsl:include and xsl:import to include other XSLT stylesheets, not XML documents. If you want to combine codes from various XML documents, you can use the document() function, which I cover in Chapter 14.

Referencing valid stylesheets

Both the xsl:include and xsl:import elements must reference a valid XSLT stylesheet in their href attribute. You can’t incorporate a file that has a snippet of XSLT code. Rather, you need to define a xsl:stylesheet element in the code and follow normal XSLT stylesheet conventions. Make sure that any stylesheet you want to reference can be called and run by itself, apart from any stylesheet that calls it.

TechnicalStuff

If you’ve worked with other programming languages before, such as C++ or Java, you’re probably familiar with an include statement to incorporate source code from one file into another. The xsl:include is similar to these uses, except that the included file must be a normal stylesheet, not part of one.

Placing the elements

Both xsl:include and xsl:import must be added as top-level elements of the calling stylesheet. xsl:import has the strictest rules for its placement: It must appear as the first child element under an xsl:stylesheet element, or you get a processing error. In contrast, xsl:include can appear anywhere within the calling stylesheet as long as it’s a top-level element. You can’t, for example, place an xsl:include element inside a template rule.

Resolving conflicts

The most important difference between xsl:include and xsl:import is the different ways they handle conflicts. A conflict occurs when a top-level element from the incoming stylesheet is identical to an element in the calling stylesheet. xsl:import handles any collisions neatly: The element from the calling stylesheet wins because it has greater import precedence, discarding the element from the referenced stylesheet. In contrast, xsl:include can’t handle conflict well: Any collision causes a processing error and so the transformation fails.

Consider, for example, the case in which a variable defined in the calling stylesheet has the same name as one in the referenced stylesheet. The referenced stylesheet looks like:

 <!-- referencesheet.xsl --> 

<xsl:stylesheet version=”1.0” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>

  <xsl:variable name=”state”>pondering</xsl:variable>

</xsl:stylesheet>

When another stylesheet uses xsl:import to call this stylesheet and has a conflicting variable name, the one in the calling stylesheet wins. So, in the following case, the state variable has the value of probing.

 <!-- callingsheet.xsl --> 

<xsl:stylesheet version=”1.0” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>

  <xsl:import href=”referencesheet.xsl”/>

  <xsl:variable name=”state”>probing</xsl:variable>

</xsl:stylesheet>

However, if xsl:include is used, you get a processing error because the variables are considered duplicates:

 <!-- callingsheet.xsl --> 

<xsl:stylesheet version=”1.0” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>

  <xsl:include href=”referencesheet.xsl”/>

  <xsl:variable name=”state”>contemplating</xsl:variable>

</xsl:stylesheet>

You have to remove one of these variable declarations before continuing.

Remember

xsl:include can potentially introduce conflict resolution problems that must be corrected before processing can continue. In contrast, a conflict in xsl:import never causes errors because the calling stylesheet always wins.

Handling identical template rules

Two template rules that have the same match pattern aren’t considered a conflict per se and don’t generate a processing error with xsl:include. I explain in Chapter 4 that XSLT allows this scenario and simply prioritizes template rules based on a system of weighting. So, in the case where a template rule from the calling stylesheet is identical to the template rule of a called stylesheet, the following rules apply:

bullet For xsl:import, the template rule in the calling stylesheet always wins.

bullet For xsl:include, the template rule that appears last wins. So if the xsl:include element comes before the template rule definition in the calling stylesheet, the calling stylesheet template rule is used. However, if the xsl:include line comes after the calling stylesheet’s template rule, the template rule from the called stylesheet wins.

Practical Use of xsl:import

When you fully understand how xsl:include and xsl:import are used and their potential implications, you can use them effectively to make your stylesheet development process more efficient. Following is a fairly basic, yet practical example of how you can use xsl:import. Suppose that a company has an XML document of employees which it uses as the data source for multiple output needs:

bullet An HTML summary report of employees

bullet A comma-delimited text file which separates each piece of employee data with a comma for importing into an external database

bullet A modified XML structure for use in another application

Listing 12-1 shows the XML source file.

Listing 12-1: company_emp.xml

<!-- company_emp.xml -->

<?xml version=”1.0”?>

<employees>

  <employee id=”101”>

    <lastname>Lamotte</lastname>

    <firstname>Mitch</firstname>

    <nickname>The Mitchster</nickname>

    <title>Director of Sales</title>

   </employee>

  <employee id=”102”>

    <lastname>Williams</lastname>

    <firstname>Tim</firstname>

    <nickname>Timmy Boy</nickname>

    <title>Director of Quality Assurance</title>

  </employee>

  <employee id=”103”>

    <lastname>Magruder</lastname>

    <firstname>Randy</firstname>

    <nickname>Randall</nickname>

    <title>Senior Engineer</title>

  </employee>

  <employee id=”104”>

    <lastname>Drohan</lastname>

    <firstname>Doug</firstname>

    <nickname>Tooltime</nickname>

    <title>Building Director</title>

  </employee>

  <employee id=”105”>

    <lastname>Burrer</lastname>

    <firstname>Phillip</firstname>

    <nickname>Flip</nickname>

    <title>Resident Physician</title>

  </employee>

</employees>

To act on this source document, I have created three stylesheets, each of which generates one of the three outputs I want.

The first stylesheet (shown in Listing 12-2) defines a template rule for outputting the employee elements into an HTML-based list. An xsl:output element is added with method=”html” to create an HTML file. A variable named reporttype is declared with a value of 1, which is used in the calling stylesheet I define later.

Listing 12-2: emp_report_1.xsl

<?xml version=”1.0”?>

<xsl:stylesheet version=”1.0” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>

  <!-- Declare HTML output -->

  <xsl:output method=”html”/>

  <!-- reporttype definition -->

  <xsl:variable name=”reporttype” select=”1”/>

  <!-- employee template rule for HTML output-->

  <xsl:template match=”employee”>

    <p><b><xsl:value-of select=”firstname”/><xsl:text> </xsl:text><xsl:value-of select=”lastname”/></b></p><xsl:text>

    </xsl:text>

    <p> * Employee ID: <xsl:value-of select=”@id”/></p>

    <p> * Serves as <xsl:value-of select=”title”/></p><xsl:text>

    </xsl:text>

    <p> * Likes to be called <i><xsl:value-of select=”nickname”/></i></p><xsl:text>

    </xsl:text>

    <hr></hr>

  </xsl:template>

  

</xsl:stylesheet> 

The second stylesheet is in Listing 12-3 and provides a template rule for creating a comma-delimited text file that lists the values of each child in the employee element. It has an xsl:output element in which the method attribute has a value of text, and its reporttype variable is set to 2.

Listing 12-3: emp_report_2.xsl

<?xml version=”1.0”?>

<xsl:stylesheet version=”1.0” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>

  <!-- Declare Text output -->

  <xsl:output method=”text”/>

  <!-- reporttype definition -->

  <xsl:variable name=”reporttype” select=”2”/>

  <!-- employee template rule for text-only output-->

  <xsl:template match=”employee”>

  <xsl:value-of select=”@id”/>,<xsl:value-of select=”firstname”/>,<xsl:value-of select=”lastname”/>,<xsl:value-of select=”title”/>,<xsl:value-of select=”nickname”/><xsl:text>

  </xsl:text>

</xsl:template>

  

</xsl:stylesheet>

The third stylesheet (shown in Listing 12-4) creates a template rule that transforms the original XML structure into a derivative XML document. The xsl:output’s method  is set to xml, while the reporttype variable is declared with a value of 3.

Listing 12-4: emp_report_3.xsl

<?xml version=”1.0”?>

<xsl:stylesheet version=”1.0” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>

  <!-- Declare XML output -->

  <xsl:output method=”xml”/>

  <!-- reporttype definition -->

  <xsl:variable name=”reporttype” select=”3”/>

  <!-- employee template rule for XML output-->

  <xsl:template match=”employee”>

  <employee>

    <id><xsl:value-of select=”@id”/></id>

    <name><xsl:value-of select=”firstname”/><xsl:text> </xsl:text><xsl:value-of select=”lastname”/></name>

    <title><xsl:value-of select=”title”/></title>

  </employee>

</xsl:template>

</xsl:stylesheet>

Each of these stylesheets can be run independently on the source document to produce the results desired, but it may be more efficient to create a master stylesheet that conditionally runs one of these three stylesheets. This is especially true when you have identical code that needs to be run on each output type.

For example, because I have created these three stylesheets that transform the employee element, I now create a “master” stylesheet that brings everything together:

<!-- company_emp.xsl -->

<xsl:stylesheet version=”1.0” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>

  <!-- Import employee template rule -->

  <xsl:import href=”emp_report_1.xsl”/>

  <!-- reportname variable declaration -->

  <xsl:variable name=”reportname”>Employee Summary</xsl:variable>

  <!-- Generate employees report -->

  <xsl:template match=”employees”>

    <!-- HTML report -->

    <xsl:if test=”$reporttype=1”>

    <h1><xsl:value-of select=”$reportname”/></h1>

    <xsl:apply-templates/>

    </xsl:if>

    <!-- Comma-delimited text report -->

    <xsl:if test=”$reporttype=2”>

      <xsl:text>id,name,title,nickname      

      </xsl:text>

      <xsl:apply-templates/>

    </xsl:if>

    <!-- XML report -->

    <xsl:if test=”$reporttype=3”>

      <xsl:text>

</xsl:text><employees_sub1>

    <xsl:apply-templates/>

     </employees_sub1>

    </xsl:if>

  </xsl:template>

</xsl:stylesheet>

The xsl:import element is used to specify which of the three reference stylesheets I want to use: emp_report_1.xsl for HTML (Listing 12-2), emp_report_2.xsl for text (Listing 12-3), or emp_report_3.xsl for XML (Listing 12-4). This href value is the only code that needs to be changed, depending on the output type desired.

The xsl:variable defines a reportname variable, which I use to generate a header for each report type in the employees template rule.

The employees template rule tests the value of the reporttype variable (defined in the referenced stylesheets) in the xsl:if statements to determine which of the three output types are desired:

bullet For HTML output, the template adds a h1 header.

bullet For text output, the field names are listed.

bullet Or for XML output, the template adds employees_sub1 as the document element of the new XML file created.

When the company_emp.xsl file is applied to the Listing 12-1 XML file using <xsl:import href=”emp_report_1.xsl”/>, the following HTML is generated:

<h1>Employee Summary</h1>

<p><b>Mitch Lamotte</b></p>

<p> * Employee ID: 101</p>

<p> * Serves as Director of Sales</p>

<p> * Likes to be called <i>The Mitchster</i></p>

<hr>

  

<p><b>Tim Williams</b></p>

<p> * Employee ID: 102</p>

<p> * Serves as Director of Quality Assurance</p>

<p> * Likes to be called <i>Timmy Boy</i></p>

<hr>

  

<p><b>Randy Magruder</b></p>

<p> * Employee ID: 103</p>

<p> * Serves as Senior Engineer</p>

<p> * Likes to be called <i>Randall</i></p>

<hr>

  

<p><b>Doug Drohan</b></p>

<p> * Employee ID: 104</p>

<p> * Serves as Building Director</p>

<p> * Likes to be called <i>Tooltime</i></p>

<hr>

  

<p><b>Phillip Burrer</b></p>

<p> * Employee ID: 105</p>

<p> * Serves as Resident Physician</p>

<p> * Likes to be called <i>Flip</i></p>

<hr>

When run with <xsl:import href=”emp_report_2.xsl”/>, the following text file is produced:

 id,name,title,nickname      

101,Mitch,Lamotte,Director of Sales,The Mitchster

102,Tim,Williams,Director of Quality Assurance,Timmy Boy

103,Randy,Magruder,Senior Engineer,Randall

104,Doug,Drohan,Building Director,Tooltime

105,Phillip,Burrer,Resident Physician,Flip

Finally, when run with <xsl:import href=”emp_report_2.xsl”/>, the XML result is:

<?xml version=”1.0” encoding=”utf-8”?>

<employees_sub1>

  <employee><id>101</id><name>Mitch Lamotte</name><title>Director of Sales</title></employee>

  <employee><id>102</id><name>Tim Williams</name><title>Director of Quality Assurance</title></employee>

  <employee><id>103</id><name>Randy Magruder</name><title>Senior Engineer</title></employee>

  <employee><id>104</id><name>Doug Drohan</name><title>Building Director</title></employee>

  <employee><id>105</id><name>Phillip Burrer</name><title>Resident Physician</title></employee>

</employees_sub1>  

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset