Menu

Combining Stylesheets with Include and Import

November 1, 2000

Bob DuCharme

Combining Stylesheets with Include and Import

The xsl:include and xsl:import instructions give you ways to incorporate XSLT stylesheets programmatically. There are two situations where this is useful.

  • Large, complex stylesheets, like large complex programs, are easier to maintain when you break them into modules with specific roles to play. In XSLT, the xsl:include and xsl:import instructions let you assemble the pieces. This modular approach also makes it possible to share parts of a stylesheet with other stylesheets that only want certain features and not the whole thing; they can just include or import the parts they need.

  • Customizing an existing stylesheet without actually editing that stylesheet is easy, because you incorporate it, and then separately override any template rules that don't do exactly what you want.

xsl:include

The xsl:include instruction is similar to the include instruction available in several programming languages. The XSLT processor replaces it with the contents of the stylesheet named in the href attribute. For example, the following makehtml.xsl stylesheet names the inlines.xsl stylesheet to incorporate that stylesheet:

<!-- makehtml.xsl -->



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 

     version="1.0">



  <xsl:include href="inlines.xsl"/>



  <xsl:template match="chapter">

   <html><xsl:apply-templates/></html>

  </xsl:template>



  <xsl:template match="para">

   <p><xsl:apply-templates/></p>

  </xsl:template>



  <xsl:template match="chapter/title">

   <h1><xsl:apply-templates/></h1>

  </xsl:template>



</xsl:stylesheet>

If inlines.xsl looks like this,


<!-- inlines.xsl -->



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 

     version="1.0">



  <xsl:template match="emphasis">

   <b><xsl:apply-templates/></b>

  </xsl:template>



  <xsl:template match="literal">

   <tt><xsl:apply-templates/></tt>

  </xsl:template>



</xsl:stylesheet>

makehtml.xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 

     version="1.0">



  <xsl:template match="emphasis">

   <b><xsl:apply-templates/></b>

  </xsl:template>



  <xsl:template match="literal">

   <tt><xsl:apply-templates/></tt>

  </xsl:template>



  <xsl:template match="chapter">

   <html><xsl:apply-templates/></html>

  </xsl:template>



  <xsl:template match="para">

   <p><xsl:apply-templates/></p>

  </xsl:template>



  <xsl:template match="chapter/title">

   <h1><xsl:apply-templates/></h1>

  </xsl:template>



</xsl:stylesheet>

The complete inlines.xsl stylesheet didn't get inserted; its contents did. In other words, everything between its xsl:stylesheet tags (the stylesheet's "emphasis" and "literal" template rules) got inserted where the makehtml.xsl stylesheet had its xsl:include instruction.

The included stylesheet must still be a complete stylesheet. Unlike the include mechanisms offered by some programming languages, the part you're including can't just be a piece of something -- it must be a complete stylesheet. (If you really do want to incorporate a fragment of a stylesheet into another one, you can use XML general entities, because after all, XSLT stylesheets are XML documents.)

An included stylesheet may in turn include other stylesheets, and they may include other stylesheets. There's no limit to the levels of inclusion that you can use, although the more you do it, the more complexity you have to keep track of.

The xsl:include element can go anywhere you want in a stylesheet, as long as it's a top-level element -- that is, a child of the xsl:stylesheet element that makes up the main body of the stylesheet. Putting an xsl:include instruction inside another element, such as an xsl:template template rule, wouldn't make sense, anyway; there would be no point to inserting another stylesheet's complete contents inside of a template rule.

The example above is simple. A large, complex stylesheet like the one that turns DocBook files into HTML (see Norm Walsh's DocBook XSL stylesheet) uses xsl:include on a larger scale. Of the 62 XSLT instructions in version 1.2's main docbook.xsl file, 38 of them are xsl:include instructions that incorporate its various components such as division.xsl and titlepage.xsl.

Using xsl:include doesn't change XSLT's approach to multiple template rules that apply to the same node. If the XSLT processor can't find one template that is more specific than another for a particular source tree node, it's an error. Using xsl:include does increase the chance of this error happening, especially if you include stylesheets that include other stylesheets, because it's harder to keep track of the full collection of template rules being grouped together.

xsl:import

The xsl:import instruction is similar to xsl:include except that instructions in the imported stylesheet can be be overridden by instructions in the importing stylesheet and in any included stylesheet. For example, the following makehtml2.xsl stylesheet tells the XSLT processor to import the inlines.xsl stylesheet. The syntax is nearly identical to the use of xml:include; you name the imported stylesheet with the href attribute.

<!-- makehtml2.xsl -->



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 

     version="1.0">



  <xsl:import href="inlines.xsl"/>



  <xsl:template match="chapter">

   <html><xsl:apply-templates/></html>

  </xsl:template>



  <xsl:template match="para">

   <p><xsl:apply-templates/></p>

  </xsl:template>



  <xsl:template match="chapter/title">

   <h1><xsl:apply-templates/></h1>

  </xsl:template>



  <xsl:template match="emphasis">

   <i><xsl:apply-templates/></i>

  </xsl:template>



</xsl:stylesheet>

If inlines.xsl looks like this,


<!-- inlines.xsl -->



<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 

     version="1.0">



  <xsl:template match="emphasis">

   <b><xsl:apply-templates/></b>

  </xsl:template>



  <xsl:template match="literal">

   <tt><xsl:apply-templates/></tt>

  </xsl:template>



</xsl:stylesheet>

makehtml2.xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 

     version="1.0">



  <xsl:template match="emphasis">

   <i><xsl:apply-templates/></i>

  </xsl:template>



  <xsl:template match="literal">

   <tt><xsl:apply-templates/></tt>

  </xsl:template>



  <xsl:template match="chapter">

   <html><xsl:apply-templates/></html>

  </xsl:template>



  <xsl:template match="para">

   <p><xsl:apply-templates/></p>

  </xsl:template>



  <xsl:template match="chapter/title">

   <h1><xsl:apply-templates/></h1>

  </xsl:template>



</xsl:stylesheet>

The inlines.xsl stylesheet's "literal" template rule was added to the normalized makehtml2.xsl stylesheet, but the same stylesheet's "emphasis" template rule was ignored. Both makehtml2.xsl and inlines.xsl have template rules with a match pattern of "emphasis," and because inlines.xsl was imported and not included, an XSLT processor will use the one in makehtml2.xsl, not the one in inlines.xsl. The XSLT processor will add emphasis element nodes to the result tree surrounded by the i start- and end-tags shown in makehtml2.xsl and not by the b tags in the imported "emphasis" template rule in inlines.xsl.

(Note that if the example above had used xsl:include instead of xsl:import, the presence of two xsl:template elements with a match pattern of "emphasis" would have been an error.)

As with inclusion, there's no limit to the levels of importing you may use. Your imported stylesheets may import other stylesheets, which may import yet more stylesheets, and so on.

Any xsl:import elements must come before any other elements from the XSLT namespace in a stylesheet. Thus the XSLT processor knows that when it finds a template rule with the same match pattern as one in an imported template rule, it can forget about the imported one and use the most recently found one.

For a more industrial-strength demonstration of the difference between xsl:include and xsl:import, here's a real example: the stylesheet I use to create HTML versions of this column. Because I'm using the DocBook DTD, I use the aforementioned DocBook stylesheets by Norm Walsh, but I wanted to make various changes to my own copy of the stylesheets. For example, Norm's stylesheets italicize all emphasis elements, but I want emphasized text within literallayout elements to be bolded and not italicized so that the important parts of sample stylesheets and XML documents stand out. (The sample stylesheets above all include sections tagged as emphasis elements.) To make these changes, I used to edit the various files included as part of Norm's stylesheet and add a comment with my name in them so that I could locate these changes if I ever had to make them again to a newer version of the stylesheet.

Since I sometimes write using a notebook computer running Linux and sometimes on a desktop computer running Windows, I had to remember which stylesheet files I edited and then copy them to the other computer. I just zipped up all the stylesheet files and unzipped them on the other machine. While this is a simple procedure, it still leaves too much room for error, and the more changes I made, the more trouble it would be to upgrade to a newer version of the DocBook stylesheets.

By using xsl:import and xsl:include, most of these problems go away. The complete main stylesheet that I now use to create an HTML version of this column looks like the following.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 

     version="1.0">



 <xsl:import href="/usr/local/lib/xml/xsl/docbook/html/docbook.xsl"/>

 <xsl:include href="dbMods.xsl"/>



</xsl:stylesheet>

All it does is import the main DocBook stylesheet file (a stylesheet that, as I mentioned, includes many component files) and then include a stylesheet that I wrote called dbMods.xsl. Now, when I want to change something in my copy of the DocBook stylesheet, I copy the appropriate xsl:template element from the source file in the /usr/local/lib/xml/xsl/docbook/html/ directly to my dbMods.xsl stylesheet and then make my changes there. Here are two of the edited xsl:template elements:

<!-- (dbMods.xml excerpt) -->



<xsl:template match="literallayout/emphasis">

  <b><xsl:apply-templates/></b>

</xsl:template>



<xsl:template match="ulink">

  <a>

    <xsl:attribute name="href">

      <xsl:value-of select="@url"/>

    </xsl:attribute>

    <!-- bd added following line -->

    <tt><xsl:value-of select="@url"/></tt> 

    <xsl:apply-templates/>

  </a>

</xsl:template>

The first adds b tags around any emphasis element that is a child of a literallayout element. When you download the DocBook stylesheet, it has a template rule with a pattern of "emphasis" that adds i tags around emphasis elements. Because the dbMods.xsl "literallayout/emphasis" template is more specific than that (it doesn't match all emphasis nodes, but only the ones that are children of literallayout elements), an XSLT processor will use it for those emphasis elements.

The second template above overrides one in the imported DocBook stylesheet. The regular DocBook version of the template for the ulink element type adds an a element that uses the source tree ulink element's url attribute as its href attribute value. However, it doesn't add any contents between the result tree's a tags, and I wanted the url value to show up there, so I added the line that puts it there between tt ("teletype") tags for the HTML result tree version. The comment about adding that line helps me to remember the difference between the original template and my altered version.

My dbMods.xsl file has many more template rules that I copied from the original DocBook stylesheet files and then revised. Taking these revisions from my Linux notebook to my Windows desktop machine is easy: I just bring the dbMods.xsl file. (The actual stylesheet file that includes docbook.xsl and imports dbMods.xsl is slightly different on the Windows machine from the one you see above because docbook.xsl is not in a directory named /usr/local/lib/xml/xsl/docbook/html/ on the Windows machine.)

When a new version of the DocBook stylesheet comes out, I'll just install it into a new directory, edit the value of the href attribute in my xsl:import element to point at the new version's docbook.xsl file, and I'll be ready to go.