XSLT 1.0 是否支持中间结果?



TL;DR:如何使用 XPath 表示<apply-templates />语句?


我有一个整理信息的模板。我想以多种不同的方式进一步处理这些信息,所以我想知道是否有一种方法可以从 XSLT 中的模板"返回"。

示例:我有一个 XHTML 代码片段:

<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>

我有一个模板可以从 HTML 代码段中提取带有href<a>标签。我想重复使用两次来预取页面添加"链接"栏,如下所示:

<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>

这在 XSLT 1.0 中可能吗?


如果它更容易,我有一个相关的问题变体,我正在使用样式表转换整个文档,然后想要使用转换后的版本。我知道我可以<xsl:include>其他文档转换样式表并编写<xsl:apply-templates select="document('other.xml')"/>,但我想进一步转换结果。

XSLT 1.0 向从 XPath 1.0 的数据模型合并的四种数据类型(字符串、数字、布尔值、节点集(添加了一种数据类型: 结果树片段 (https://www.w3.org/TR/xslt-10/#section-Result-Tree-Fragments(:

此附加数据类型称为结果树片段。一个变量 可能绑定到结果树片段,而不是四个片段之一 基本 XPath 数据类型(字符串、数字、布尔值、节点集(。一个结果 树片段表示结果树的片段。结果树 片段等效地处理为仅包含 单根节点。但是,允许对结果树执行的操作 片段是节点集上允许的片段的子集。一个操作 仅当该操作是 允许对字符串(对字符串的操作可能首先涉及 将字符串转换为数字或布尔值(。特别是,它是 不允许在结果树上使用///[]运算符 碎片。

因此,您可以将中间结果作为结果树片段,但如果您希望使用超过xsl:copy-ofxsl:value-of或获取字符串值的任何内容,则需要超越 XSLT 1.0,并且在大多数 XSLT 1.0 处理器中,您支持像exsl:node-set这样的扩展函数来做到这一点,它会将结果树片段转换为节点集。

下面是一个简单的示例,它首先处理输入中的一些元素以添加属性,并使用exsl:node-set(http://exslt.org/exsl/functions/node-set/index.html( 将该结果树片段转换为节点集,然后在两种不同的模式下使用该节点集两次:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="exsl msxml"
version="1.0">
<xsl:output method="html" indent="yes" version="5" doctype-system="about:legacy-doctype"/>
<xsl:variable name="numbered-items-rtf">
<xsl:apply-templates select="//item" mode="number"/>
</xsl:variable>
<xsl:variable name="numbered-items" select="exsl:node-set($numbered-items-rtf)/item"/>
<xsl:template match="item" mode="number">
<xsl:copy>
<xsl:attribute name="index">
<xsl:number/>
</xsl:attribute>
<xsl:copy-of select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
<style>
nav ul li { display: inline }
nav ul { list-item-type: none }
</style>
</head>
<body>
<h1>Example</h1>
<nav>
<ul>
<xsl:apply-templates select="$numbered-items" mode="nav"/>
</ul>
</nav>
<section>
<h2>List</h2>
<ul>
<xsl:apply-templates select="$numbered-items"/>
</ul>
</section>
</body>
</html>
</xsl:template>
<xsl:template match="item">
<li id="item-{@index}">
<xsl:apply-templates/>
</li>
</xsl:template>
<xsl:template match="item" mode="nav">
<li>
<a href="#item-{@index}">
<xsl:apply-templates/>
</a>
</li>
</xsl:template>
</xsl:stylesheet>

示例输入,例如

<root>
<items>
<item>foo</item>
<item>bar</item>
<item>baz</item>
</items>
</root>

转换为

<!DOCTYPE html SYSTEM "about:legacy-doctype">
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=utf-16">
<title>.NET XSLT Fiddle Example</title>
<style>
nav ul li { display: inline }
nav ul { list-item-type: none }
</style>
</head>
<body>
<h1>Example</h1><nav><ul>
<li><a href="#item-1">foo</a></li>
<li><a href="#item-2">bar</a></li>
<li><a href="#item-3">baz</a></li>
</ul></nav><section><h2>List</h2>
<ul>
<li id="item-1">foo</li>
<li id="item-2">bar</li>
<li id="item-3">baz</li>
</ul></section></body>
</html>

https://xsltfiddle.liberty-development.net/pPqsHUd/1

缺点是某些XSLT 1.0处理器不支持exsl:node-set功能,但在专有命名空间中具有类似的功能(例如Microsoft基于COM的MSXML(3,4,5,6(处理器仅支持命名空间xmlns:msxml="urn:schemas-microsoft-com:xslt"中的msxml:node-set,就像(过时的(基于.NET的XslTransform一样(。只要您面向单个 XSLT 1.0 处理器,您当然可以调整您的代码以使用正确的命名空间/扩展函数,但如果您想针对不同的命名空间/扩展函数,您将很难找到一种紧凑而优雅的方式来使用基于function-available的不同扩展函数,因为您在 XPath 1.0 中没有if表达式。

因此,https://xsltfiddle.liberty-development.net/pPqsHUd/2 适用于例如Chrome和Firefox等Mozilla浏览器,因为这些浏览器确实支持exsl:node-set但在IE和Edge Microsoft失败,因为它们使用MSXML并且不支持exsl:node-set,对于它们,您需要像 https://xsltfiddle.liberty-development.net/pPqsHUd/3 一样<xsl:variable name="numbered-items" select="msxml:node-set($numbered-items-rtf)/item"/>

在IE中,您可以使用脚本扩展使其支持exsl:node-set但不幸的是,在Edge中这不起作用:https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7598626/。

最新更新