【问题标题】:Does XSLT 1.0 support intermediate results?XSLT 1.0 是否支持中间结果?
【发布时间】:2018-10-23 10:52:50
【问题描述】:

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


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

示例:我有一个 XHTML sn-p:

<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 sn-p 中提取带有href&lt;a&gt; 标记。我想重复使用两次来预取页面添加一个“链接”栏,如下所示:

<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 中是否可行?


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

【问题讨论】:

  • 那么,您如何使用该模板“从 HTML sn-p 中提取带有 href 的 &lt;a&gt; 标记”看起来准确,它返回什么?您希望能够以什么方式使用中间结果两次?当然,给定 XSLT 1,您是否希望在使用或不使用 exsl:node-set 之类的扩展函数的情况下进一步处理节点形式的中间结果?您当然可以推送相同的 XPath 选择(例如//xhtml:a[@href]) 到不同的模板,使用模式将相同的输入节点两次或多次转换为不同的输出。
  • @MartinHonnen 该模板实际上是&lt;xsl:copy-of select="html:a[@href]" /&gt;,但我希望它适用于更复杂的示例。
  • 那没有意义,输入有一些没有真正拼出的命名空间,你显示的结果没有任何命名空间。所以一个简单的xsl:copy-of 永远不会处理特定命名空间中的输入以获得没有命名空间的结果。当然,另一方面,如果你想要的只是选择输入节点,那么你不需要模板使用变量&lt;xsl:variable name="links" select="html:a[@href]"/&gt;,然后将变量推送到具有不同模式的不同模板以构建不同的演示文稿。跨度>
  • @MartinHonnen xsl:variable 定义非常适合 XPath 表达式,但是如果我想在其他地方定义 XPath 表达式怎么办?如果我想要比简单的 XPath 表达式更复杂的东西怎么办?
  • 我认为我们不能而且应该在这里重写有关 XSLT 的教程,例如参见第 3 章 XPath 数据模型中的书 cranesoftwrights.github.io/books/ptux/index.htm,解释了 XSLT 1 的限制,其中“不能推或拉”结果树片段的节点”。但正如我已经指出的,有像exsl:node-set 这样的扩展函数可以将结果树片段转换为节点集。因此,对于某个 XSLT 1.0 处理器,您通常可以使用这样的功能。

标签: xml xslt xpath foreach return


【解决方案1】:

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

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

因此,您可以将中间结果作为结果树片段,但是如果您希望使用除 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) 处理器仅支持 msxml:node-set在命名空间xmlns:msxml="urn:schemas-microsoft-com:xslt" 中,就像(过时的)基于.NET 的XslTransform 一样。只要您针对单个 XSLT 1.0 处理器,您当然可以调整您的代码以使用正确的命名空间/扩展功能,但是如果您想针对不同的,您将很难找到一种紧凑而优雅的方式来使用基于不同的扩展功能function-available 因为您在 XPath 1.0 中没有 if 表达式。

所以https://xsltfiddle.liberty-development.net/pPqsHUd/2 适用于例如Chrome 和 Mozilla 浏览器(如 Firefox)支持 exsl:node-set,但在 Microsoft IE 和 Edge 中失败,因为它们使用 MSXML 且不支持 exsl:node-set,对于它们,您需要 &lt;xsl:variable name="numbered-items" select="msxml:node-set($numbered-items-rtf)/item"/&gt;,就像在 https://xsltfiddle.liberty-development.net/pPqsHUd/3 中所做的那样。

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

【讨论】:

    猜你喜欢
    • 2015-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-21
    • 1970-01-01
    • 2012-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多