XSLT 1.0是否支持中间结果?

时间:2018-10-23 10:52:51

标签: xml xslt xpath foreach return

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代码段中提取带有<a>的{​​{1}}标签。我想重复使用两次以预取页面 并添加“链接”栏,如下所示:

href

在XSLT 1.0中可能吗?


如果比较容易,我可以通过使用样式表来转换整个文档,然后使用转换后的版本来解决该问题。我知道我可以<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> 转换其他文档的样式表并编写<xsl:include>,但是我想进一步转换其结果。

1 个答案:

答案 0 :(得分:2)

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-sethttp://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)处理器仅支持{名称空间msxml:node-set中的{1}},就像(过时的)基于.NET的xmlns:msxml="urn:schemas-microsoft-com:xslt"一样)。当然,只要您瞄准单个XSLT 1.0处理器,您就可以使您的代码适应使用正确的名称空间/扩展功能,但是如果您要针对不同的名称/扩展功能,则将难以找到一种紧凑而优雅的方式来基于以下功能使用不同的扩展功能XslTransform,因为您在XPath 1.0中没有function-available表达式。

因此https://xsltfiddle.liberty-development.net/pPqsHUd/2适用于Chrome和Firefox等Mozilla浏览器确实支持if,但它们在Microsoft IE和Edge中却无法使用,因为它们使用MSXML并且不支持exsl:node-set,因为它们需要exsl:node-set才能完成在https://xsltfiddle.liberty-development.net/pPqsHUd/3中。

在IE中,您可以使用脚本扩展名使其支持<xsl:variable name="numbered-items" select="msxml:node-set($numbered-items-rtf)/item"/>,但不幸的是,在Edge中,此扩展名无效:https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7598626/