Jeni Tennison
>Let's say I have an XMl file which has an element such as:
><include url="file.xml"/>
>This file would be part of the "input tree".
>You would probably say that xsl:include would do the trick, yes but that's
>not my question.
Actually, from your description of the problem, we wouldn't say that
xsl:include would do the trick. xsl:include, like xsl:import is about
including/importing *stylesheets* into an XSLT stylesheet. In other words,
xsl:include/xsl:import are what you would use if you wanted to build
modular stylesheets; they aren't what you use to pull in more information
for processing. For that, you use the document() function. For example,
you can use document() to pull in information from a particular file and
process it: <xsl:apply-templates select="document('file.xml')" mode="include" /> If you have a filename specified within your source XML, such as with your
include: <include url="file.xml" /> you can use XPath to access the name of the file, and document() to access
its content:
<xsl:apply-templates select="document(include/@url)" mode="include" />
>We want to make these inclusions conditional, and than work on that result
>tree.
><if include="1">
><then>
><include url="file1.xml"/>
></then>
><else>
><include url="file2.xml"/>
></else>
></if>
It's not particularly clear to me whether the specification of these
conditions is done in your source XML or in your stylesheet. You could
specify this in your stylesheet using something like:
<xsl:choose>
<xsl:when test="$include = 1">
<xsl:apply-templates select="document('file1.xml')" mode="include" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="document('file2.xml')" mode="include" />
</xsl:otherwise>
</xsl:choose>
If the above XML is in your source file, then you need a template that
matches 'if' and decides what to do based on its contents:
<xsl:template match="if">
<xsl:choose>
<xsl:when test="@include = $include">
<xsl:apply-templates select="document(then/include/@url)"
mode="include" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="document(else/include/@url)"
mode="include" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
In the above, I've used xsl:apply-templates on the result of the call to
document() in 'include' mode. The reason for this is that document()
returns the root node of the XML document that you're accessing, which will
match a template that matches the root node, which is often used as the
base template in a stylesheet. So, if you had, for example, a template like: <xsl:template match="/">
<xsl:apply-templates select="document('file.xml')" />
</xsl:template>
you'd cause an infinite loop: the template is matched by the root node of
the source document, then templates are applied to the root node of
file.xml, which fires the template again, so templates are applied to the
root node of file.xml, which fires the template again and so on.
Adding a mode means that you can do: <xsl:template match="/">
<xsl:apply-templates select="document('file.xml')" mode="include" />
</xsl:template>
<xsl:template match="/" mode="include">
<!-- do something else -->
</xsl:template>
Another way of getting round the problem is to apply templates to the
document element of the accessed document rather than its root node
(although this only works if the document elements of the accessed document
and source document aren't the same).
Of course, you don't have to apply templates at all: you can assign the
root node of the accessed document to a variable and index into it as you
desire, or use xsl:for-each to iterate over its contents.
Mike Kay: This has been raised a number of times. On a previous thread we came to the
conclusion that the user was trying to write a general-purpose stylesheet G
and then specialize it by conditionally including a special-purpose
stylesheet A or B. The way to meet this requirement is to have A and B
include G, not the other way around, and then you conditionally select A or
B as the principal stylesheet when starting the transformation. |