Поддерживает ли XSLT 1.0 промежуточные результаты?

TL;DR: как я могу представлять <apply-templates /> заявление с XPath?


У меня есть шаблон, который собирает информацию. Я хотел бы в дальнейшем обрабатывать эту информацию несколькими разными способами, поэтому мне было интересно, есть ли способ сортировки "возврата" из шаблона в 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>

У меня есть шаблон для извлечения <a> теги с href из фрагмента HTML. Я хотел бы использовать это дважды для предварительной выборки страниц и добавления строки "Связано", например:

<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')"/>, но я хочу еще трансформировать результат этого.

1 ответ

Решение

XSLT 1.0 добавляет один тип данных к четырем (строка, число, логическое значение, набор узлов), включенных в модель данных XPath 1.0: фрагменты дерева результатов ( https://www.w3.org/TR/xslt-10/):

Этот дополнительный тип данных называется фрагментом дерева результатов. Переменная может быть связана с фрагментом результирующего дерева вместо одного из четырех основных типов данных XPath (строка, число, логическое значение, набор узлов). Фрагмент результирующего дерева представляет собой фрагмент результирующего дерева. Фрагмент дерева результатов обрабатывается эквивалентно набору узлов, который содержит только один корневой узел. Однако операции, разрешенные для фрагмента дерева результатов, являются подмножеством операций, разрешенных для набора узлов. Операция разрешена для фрагмента результирующего дерева, только если эта операция будет разрешена для строки (операция со строкой может включать в себя сначала преобразование строки в число или логическое значение). В частности, запрещено использовать /, //, а также [] операторы на фрагментах дерева результатов.

Таким образом, вы можете иметь промежуточные результаты в виде фрагментов дерева результатов, но если вы планируете использовать что-то большее, чем xsl:copy-of или же xsl: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 функционируют, но похожи в проприетарном пространстве имен (например, процессоры MSXML (3,4,5,6), основанные на COM от Microsoft, поддерживают только msxml:node-set в пространстве имен xmlns:msxml="urn:schemas-microsoft-com:xslt", как делает (устарела) .NET на основе XslTransform). Пока вы нацелены на один процессор XSLT 1.0, вы, конечно, можете адаптировать свой код для использования правильной функции пространства имен / расширения, но если вы хотите использовать разные функции, вам будет сложно найти компактный и элегантный способ использования различных функций расширения на основе function-available как у вас нет if выражение в XPath 1.0.

Так что https://xsltfiddle.liberty-development.net/pPqsHUd/2 работает, например, с Chrome и с браузерами Mozilla, такими как Firefox, поскольку эти браузеры поддерживают exsl:node-set но терпит неудачу в Microsoft IE и Edge, поскольку они используют MSXML и не поддерживают exsl:node-setдля них тебе нужно <xsl:variable name="numbered-items" select="msxml:node-set($numbered-items-rtf)/item"/> как это сделано в https://xsltfiddle.liberty-development.net/pPqsHUd/3.

В IE вы можете использовать расширение скрипта для поддержки exsl:node-set но, к сожалению, в Edge это не работает: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7598626/.

Другие вопросы по тегам