xslt import, importing

Import

1. Import, general vs specialist
2. xsl:import
3. File import
4. xsl:import vs xsl:include
5. How to include a header in a stylesheet
6. How to use xsl:import
7. Import relative to xsl stylesheet directory
8. Reliance on import precedence considered dangerous
9. xsl:import, can it be made dynamic
10. Using variables with xsl:import
11. Understanding Import precedence

1.

Import, general vs specialist

Wendell Piez

The most important thing to understand about xsl:import is the notion of "import precedence". You'll find this documented in any reasonably complete guide to the language. Basically the idea is that a stylesheet's own template rules override those in any imported stylesheet (in order based on the sequence in which they are imported). This makes it easy to build a modular arrangement of stylesheets in which the more generic rules are provided by "lower-level" (imported stylesheets), and the more specific rules belonging to a specific document type or subtype, or specific mode of handling the documents, appear at the top-level.

So if your generic rules are in stylesheet A.xsl, you can have

A1.xsl imports A.xsl
A2.xsl imports A.xsl

where A1 and A2 each provide different "customization layers" over A.

So if you have scenarios A1, A2 and A3, instead of invoking A.xsl at runtime and providing a parameter to distinguish between the cases, you simply invoke A1.xsl, A2.xsl, or A3.xsl, whichever you need. Templates common to all of them can be maintained in A.xsl; each of A1 - A3 can have the custom code its processing scenario requires.

There are several advantages we get from this approach. For one, templates in the different customization layers are easily distinguished; you don't need special "case statements" in any templates, since the differentiation between the cases has already been accomplished at the top level. The customizing layers can each provide their own templates for special handling, and don't have to knock elbows with the processing code belonging to the other scenarios. And they can override the common layer more or less drastically, depending.

Consider:

source document

<speech><sp>Miranda</sp>
<l>O, wonder!</l>
<l>How many goodly creatures are there here!</l> <l>How beauteous mankind
is! O <emph>brave new world</emph>,</l> <l>That has such people in't!</l>
</speech>

in A.xsl:

<xsl:template match="speech">
   <div>
     <xsl:apply-templates/>
   </div>
</xsl:template>

<xsl:template match="sp"/>

<xsl:template match="emph">
   <i>
     <xsl:apply-templates/>
   </i>
</xsl:template>

in A1.xsl:

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

<xsl:template match="l">
   <h1>
     <xsl:apply-templates/>
   </h1>
</xsl:template>

<xsl:template match="sp">
   <h2 style="color:green">
     <xsl:apply-templates/>
   </h2>
</xsl:template>

in A2.xsl:

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

<xsl:template match="l">
   <h2>
     <xsl:apply-templates/>
   </h2>
</xsl:template>

in A3.xsl:

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

<xsl:template match="l">
   <h3>
     <xsl:apply-templates/>
   </h3>
</xsl:template>

If you run A1.xsl, A2.xsl or A3.xsl, the lines in the speech will come out as h1, h2 or h3 depending, but in all of them, the speech will appear as a <div>, and the emph will appear mapped to <i> (having matched the templates in A.xsl). Additionally, the speaker ("Miranda") will appear in a green h2 -- but only from A1.xsl; in the others, it's suppressed. (A1.xsl provides a better match for the 'sp' node due to its higher import precedence than A.xsl; the others don't.)

I think you can probably extrapolate from this description to see how it could apply in your case. I should think it would reduce the complexity of your templates considerably, as well as making reuse and local customization that much easier.

Topics to research relating to this would include "import precedence", xsl:import and xsl:apply-imports.

2.

xsl:import

David Marston

>does the xsl:import statment
>accept a relative URL ???

It does.

Meaning I can use

<xsl:import href="../../file.ext">

3.

File import

Mike Kay

Q expansion: I have two xsl files, the first that import the second. In the second xsl file I have a table, and I want to change the color of the table based on a variable contained in the first xsl file. I defined an xsl variable called 'bgcolor' in the following way:

<xsl:variable name="foo"
select="White"/> 

Then in the second xsl file I have

 <TD> <xsl:attribute name="bgcolor">

and I have to put here the value of the variable called 'foo' as value of the attribute

try:

<xsl:attribute name="bgcolor">
  <xsl:value-of select="$foo"/>
</xsl:attribute>

            

4.

xsl:import vs xsl:include

Tony Graham

There are differences. Stylesheets that you reference with xsl:import have a lower "import precedence" than the importing stylesheet, whereas stylesheets you reference with xsl:include are almost completely treated as if you'd copied the referenced file and pasted it into your stylesheet at that point.

For example, if you had template in both the main and the referenced stylesheet with the same match pattern. With xsl:import, you wouldn't get a conflict because the main stylesheet has higher "import precedence" (plus there's rules about import precedence of multiple imported stylesheets). With xsl:include, you would get an error because the template in the referenced stylesheet is treated as if it were in the main stylesheet, and two templates matching the same pattern will cause an error. Mike Kay added: The differences are to do with the handling of precedence if several stylesheets declare the same object.

I quite agree that it seems crazy to have two such strongly overlapping features: it looks from the outside like a classic committee compromise, and frankly the reason I delayed implementing it for so long in SAXON was that I thought any sensible committee (is there such a beast?) would rationalise the spec in this area. My optimism seems to have been unfounded.

5.

How to include a header in a stylesheet

David Carlisle

You can do the following:

<!DOCTYPE xsl:stylesheet [
<!ENTITY header SYSTEM "header.txt">
]>

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



<xsl:template match="foo">
&header;
<xsl:apply-templates/>
</xsl:template>


</xsl:stylesheet>

however note that

1) If your xsl engine uses a validating parser on the XSL file, you need to add a SYSTEM entry to that DOCTYPE pointing at a DTD that matches the stylesheet you are using.

2) The header `text' file is still parsed as XML, so you still need to be careful of quoting < and &.

3) This is using general XML features rather than an XSL include mechanism, it is probably easier to put your header into a named template in an xsl file and then use the XSL import mechanism to include this into your stylesheets, then just access the header by calling the named template.

6.

How to use xsl:import

Steve Tinney



> I'm trying to determine the type of the current node and print
> out an appropriate message. 

I think (someone please prove me wrong) it is impossible to do this directly.

What you can do, however, is this:

nodetype.xsl:
=============
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="node-type">
  <xsl:param name="node" select="."/>
  <xsl:apply-templates mode="nodetype" select="$node"/>
</xsl:template>
<xsl:template mode="nodetype" match="*">element</xsl:template>
<xsl:template mode="nodetype" match="@*">attribute</xsl:template>
<xsl:template mode="nodetype" match="text()">text</xsl:template>
</xsl:stylesheet>

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

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

<xsl:template match="/"> 
  <xsl:for-each select="//node()|//@*">
    <xsl:variable name="node-type">
      <xsl:call-template name="node-type"/>
    </xsl:variable>
    <xsl:message>Node is of type: <xsl:value-of select="$node-type"
                 /></xsl:message>
  </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

test.xml:
=========
<test>
  <elem type="attr">hello</elem>
  world
</test>

output:
=======
$do-saxon2 test.xml test.xsl
Node is of type: element
Node is of type: text
Node is of type: element
Node is of type: attribute
Node is of type: text
Node is of type: text

In Real Life(TM), you can just test the string value in your xsl:choose instead of testing the node type directly.

7.

Import relative to xsl stylesheet directory

Steve Tinney

>  <xsl:import href="teihtml-param.xsl"/>
> 
> should be interpreted relative to the importing stylesheet?

Should be (XSLT 2.6.2): "A relative URI is resolved relative to the base
URI of the xsl:import element".  

8.

Reliance on import precedence considered dangerous

Jeni Tennison

I think I've discovered an odd behaviour concerning imported stylesheets that I haven't seen mentioned anywhere. I'd be very glad to be proved wrong (on both it's factual or it's status as a known problem that David first identified 18 months ago).

When you import the same utility stylesheet (D) into two stylesheets (B and C) that you then import into a main stylesheet (A), any templates, attribute sets, global variables or parameters (and so on for a few other things) in the first stylesheet that's imported (B) that override the ones in the utility stylesheet (D) will no longer work as they would if the stylesheet (B) were run standalone.

Here is an illustrative example.

In preparation for XSLT UK, I'm putting together a list of hostelries that may be good locations for vehement arguments on the use of xsl:script. I have a neat little utility function that uses the Piez Technique to output the rating of a hostelry as a number of *s:

<xsl:template match="city[name = 'Oxford']/hostelry" mode="rating">
   <xsl:for-each select="$loadsanodes[position() &lt;= @rating]">
      <xsl:text>*</xsl:text>
   </xsl:for-each>
</xsl:template>

and this I have placed in a utility stylesheet unimaginatively called 'utilities.xsl'.

I then have two stylesheets that can stand alone if necessary. One called 'pubs.xsl' and one called 'restaurants.xsl'. These create tables of the pubs and restaurants, including their rating. In honour of David Carlisle's penchant for OP, there's a special template of pubs.xsl that overrides the one above:

<xsl:template match="city[name = 'Oxford']/
                        hostelry[drink = 'Old Peculier']"
              mode="rating">
   <xsl:for-each select="$loadsanodes[position() &lt;= @rating]">
      <xsl:text>D</xsl:text>
   </xsl:for-each>
</xsl:template>

pubs.xsl works hunkydory on its own - good places to get David that pint you owe him are labelled appropriately. However, when I import both pubs.xsl and restaurant.xsl into my main stylesheet:

<xsl:import href="pubs.xsl" />
<xsl:import href="restaurant.xsl" />

Suddenly they stop being labelled! Why? Well, looking at the import tree, utilities.xsl imported by restaurant.xsl has a higher import precedence than pubs.xsl. Since the two templates have the same priority, the one with the highest import precedence wins - the one in utilities.xsl. To make it work I have to add an explicit priority to the template in pubs.xsl.

If this is the case, perhaps it is something that should be added in a Note in Section 2.6.2 of the XSLT 1.1 WD? It's not error-producing like the equivalent with xsl:include, but it might be unexpected.

9.

xsl:import, can it be made dynamic

David Carlisle


> That is to say, I need  the import to be dynamic.

xsl:import is a compile time include so it can't depend on the value of any expression.

10.

Using variables with xsl:import

David Carlisle

If I'm using xsl:import (which I do, mostly) I usually try to make sure that any variables are defined in each stylesheet, either by having an explicit xsl:variable... or xsl:importing some stylesheet that defines the variable.

when pulling together a complete stylesheet by importing all the components, the import precedence rules will mean that all but one of the variable definitions will be ignored so you just have to make sure that the one you want is the one with highest precedence.

If you use xsl:include however, multiple definitions of the same variable would lead to redfinition errors and so there you need to make sure that your component stylesheets _don_t (re)define the variables.

11.

Understanding Import precedence

David Carlisle



> It just seems really strange that the ordering of the import 
> statements should be more important that the priority attribute.

I think that the reason for xsl:import's existence is that you should be able to take 10000000000 lines of Norm's docbook stylesheets, xsl:import them and then define a couple of your own templates for specific elements where you want your own processing. You can be sure that your templates will win, even if you have not fully digested the publicly available stylesheets that you were importing.

If you had to know the priorities on the templates that you wanted to override, you'd have to understand the stylesheets well enough to work out which templates were being used, and with what priority, and then specify a higher one. (well actually it would be easier just to keep adding 0's to the end of the priority until you won:-)