TL;DR: How can I represent an <apply-templates />
statement with XPath?
I've got a template that collates information. I'd like to further process this information in more than one different way, so I was wondering whether there was a way to sort of "return" from a template in XSLT.
Example: I've got an XHTML snippet:
<page html:xmlns="html namespace">
<html:p>
The <html:a href="/foo">Tropical Foo</html:a> uses <html:a href="bar-language">Bar</html:a> to implement <html:a href="/programs/fizzbuzz>FizzBuzz</html:a>
</html:p>
</page>
I've got a template to extract <a>
tags with an href
from an HTML snippet. I'd like to reuse this twice to both prefetch the pages and add a "Linked" bar, like so:
<html>
<head>
<link rel="prefetch" href="/foo" />
<link rel="prefetch" href="bar-language" />
<link rel="prefetch" href="/programs/fizzbuzz" />
</head>
<body>
<main>
<p>
The <a href="/foo">Tropical Foo</a> uses <a href="bar-language">Bar</a> to implement <a href="/programs/fizzbuzz>FizzBuzz</a>
</p>
</main>
<aside>
<h2>Linked</h2>
<ul>
<li><a href="/foo">Tropical Foo</a></li>
<li><a href="bar-language">Bar</a></li>
<li><a href="/programs/fizzbuzz>FizzBuzz</a></li>
</ul>
</aside>
</body>
</html>
Is this possible in XSLT 1.0?
If it's easier, I've got a related variant of the problem where I'm transforming an entire document with a stylesheet, and then want to use the transformed version. I know I can <xsl:include>
the other-document-transforming stylesheet and write <xsl:apply-templates select="document('other.xml')"/>
, but I want to further transform the result of this.
XSLT 1.0 adds one data type to the four (string, number, boolean, node-set) incorporated from the data model of XPath 1.0: result tree fragments (https://www.w3.org/TR/xslt-10/#section-Result-Tree-Fragments):
So you can have intermediary results as result tree fragments but if you expect to use anything more than
xsl:copy-of
orxsl:value-of
or taking the string value you need to go beyond XSLT 1.0 and in most XSLT 1.0 processors you have support for an extension function likeexsl:node-set
to do that, it converts a result tree fragment to a node-set.Here is a simple example that first processes some elements from an input to add an attribute and converts that result tree fragment into a node-set using
exsl:node-set
(http://exslt.org/exsl/functions/node-set/index.html) to then use that node-set twice for two different modes:A sample input like
is transformed to
https://xsltfiddle.liberty-development.net/pPqsHUd/1
The draw back is that some XSLT 1.0 processors don't support the
exsl:node-set
function but a similar in a proprietary namespace (for instance Microsoft's COM based MSXML (3,4,5,6) processors only supportmsxml:node-set
in the namespacexmlns:msxml="urn:schemas-microsoft-com:xslt"
, like does the (obsolete) .NET basedXslTransform
). As long as you target a single XSLT 1.0 processor you can of course adapt your code to use the right namespace/extension function but if you want to target different ones you will struggle to find a compact and elegant way to use different extension functions based onfunction-available
as you don't have anif
expression in XPath 1.0.So https://xsltfiddle.liberty-development.net/pPqsHUd/2 works with e.g. Chrome and with Mozilla browsers like Firefox as these browsers do support
exsl:node-set
but fails in Microsoft IE and Edge as these use MSXML and don't supportexsl:node-set
, for them you need<xsl:variable name="numbered-items" select="msxml:node-set($numbered-items-rtf)/item"/>
as done in https://xsltfiddle.liberty-development.net/pPqsHUd/3.In IE you can use a script extension to have it support
exsl:node-set
but unfortunately in Edge this doesn't work: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7598626/.