Фрагмент дерева результатов для набора узлов: универсальный подход для всех движков xsl

Отвечая на другой поток (см. Stackru: генерируйте цветовые схемы css), я столкнулся с проблемой ниже, где различные механизмы xsl, похоже, нуждаются в разных подходах при преобразовании фрагментов дерева результатов в наборы узлов.

Упрощая проблему (но см. Ссылку выше для полной истории), я хочу иметь встроенное дерево, содержащее список значений цвета. Поскольку это должно использоваться в выражениях Xpath, мне пришлось создать из него набор узлов специально для движка MSXML xx xsl (встроенному XML Spy было меньше проблем с интерпретацией переменных выражения Xpath, создаваемых как rtf).
В этом мне помог еще один поток /questions/14277041/avtomatizatsiya-exsltnode-set. Результирующий набор узлов используется при создании новой переменной rtf из входного XML.
Опять же, MSXML жалуется, когда новая переменная используется в выражениях Xpath, поэтому я использовал функцию набора узлов, чтобы создать из нее набор узлов.
Пока все хорошо, и MSXML xx больше не дает ошибок.
Но когда я запускаю то же самое во встроенном XML Spy или в Saxon 9he, я получаю еще одну ошибку: кажется, что функция набора узлов неизвестна:

Cannot find a matching 1-argument function named {urn:schemas-microsoft-com:xslt}node-set() in variable colorList

Обратите внимание, что этот двухэтапный подход не нужен в данном конкретном примере, но, как я уже сказал, я упростил вещи; Я просто хочу знать, как написать преобразование XSLT 1.0, которое будет работать во всех движках xsl.

XSLT, который я использовал:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:std="http://whatever"
    xmlns:exslt="urn:schemas-microsoft-com:xslt"
    exclude-result-prefixes="std exslt">

    <xsl:output method="xml" indent="yes"/>

    <std:colors>
        <color>#0000FF</color>
        <color>#FF0000</color>
    </std:colors>

    <xsl:variable name="colors" select="document('')/*/std:colors"/>

    <xsl:variable name="std:colorList">
        <xsl:for-each select="//testid">
            <xsl:variable name="pos" select="position() mod 2"/>
            <xsl:element name="color">
                <xsl:attribute name="testid"><xsl:value-of select="."/></xsl:attribute>
                <xsl:value-of select="$colors/color[$pos + 1]"/>
            </xsl:element>
        </xsl:for-each>
    </xsl:variable>

    <xsl:variable name="colorList" select="exslt:node-set($std:colorList)"/>

    <xsl:template match="/">
        <output>
            <xsl:copy-of select="$colorList/color"/>
        </output>
   </xsl:template>

</xsl:stylesheet>

Входной файл:

<?xml version="1.0" standalone="yes"?>
<NewDataSet>
  <defects>
    <testid>111</testid>
  </defects>
  <defects>
    <testid>999</testid>
  </defects>
</NewDataSet>

Результат в MSXML 3.0/4.0/6.0:

<?xml version="1.0" encoding="UTF-16"?>
<output>
<color testid="111">#FF0000</color>
<color testid="999">#0000FF</color>
</output>

Результат в Saxon9he:

Cannot find a matching 1-argument function named {urn:schemas-microsoft-com:xslt}node-set()
in variable colorList

результат в XML Spy встроенный движок xsl:

Error in XPath expression
Unknown function - Name and number of arguments do not match any function signature in the static context - 'urn:schemas-microsoft-com:xslt:node-set'

5 ответов

Решение

Для процессоров, отличных от MSXML, используйте функцию exslt:node-set(). (Http://www.exslt.org/). (Немного странно связывать префикс exslt с версией функции Microsoft - это меня некоторое время смущало!)

Вы можете проверить, какие функции доступны, используя function-available():

<xsl:choose>
  <xsl:when test="function-available('exslt:node-set')"...
  <xsl:when test="function-available('msxsl:node-set')"...

Для Saxon-HE и других процессоров XSLT 2.0 вам не нужны никакие из этих функций, поэтому используйте

<xsl:when test="xsl:version='2.0'">

Чтобы избежать необходимости делать

 <xsl:choose>
 <xsl:when test="function-available('exslt:node-set')"...

Каждый раз, когда вам это нужно, вы можете определить набор узлов exslt для механизмов xslt2 (используя xsl:function) и для msxml (используя msxsl:script), а затем просто использовать функцию exslt:node-set в остальной части вашего документа.

http://dpcarlisle.blogspot.co.uk/2007/05/exslt-node-set-function.html

показывает, как определить его для msxml и для движков xslt2, вы можете xsl: включить таблицу стилей

<xsl:stylesheet version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:exslt="http://exslt.org/common"
        >

<xsl:function name="exslt:node-set">
  <xsl:param name="rtf"/>
  <xsl:sequence select="$rtf"/>
</xsl:function>

<xsl:stylesheet>

Правила прямой совместимости XSLT1 означают, что включить это безопасно, механизм XSLT1 просто проигнорирует это.

Microsoft.NET XsltCompiledTransform XSLT-процессор поддерживает exslt:node-set().

Для MSXML можно использовать мою собственную реализацию подмножества функций EXSLT - для MSXML. Вы можете найти подробное описание и ссылку для скачивания здесь:

http://www.xml.com/pub/a/2003/08/06/exslt.html

Что ж, Saxon 9 - это процессор XSLT 2.0, а в XSLT 2.0 одно из главных улучшений состоит в том, что различие между наборами узлов и фрагментами дерева результатов прошло, и вам вообще не нужны никакие функции расширения, так как вам не нужно форсировать любое принуждение больше. Таким образом, при использовании таблиц стилей, предназначенных для любого процессора XSLT 2.0, вы должны просто отбросить любую попытку использовать такую ​​функцию расширения, тогда таблица стилей будет работать. Если вы хотите запустить одну и ту же таблицу стилей с процессорами XSLT 1.0 и 2.0, я вижу проблему, но не думаю, что есть простое решение. Вам нужно будет использовать http://www.w3.org/TR/xslt и system-property для различения процессоров и доступности функций расширения.

Ну, вы можете легко сделать это без каких-либо проверок. Просто следуйте шаблону, описанному здесь: http://dpcarlisle.blogspot.co.uk/2007/05/exslt-node-set-function.html

<xsl:stylesheet
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exslt="http://exslt.org/common"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt"
  exclude-result-prefixes="exslt msxsl">


<msxsl:script language="JScript" implements-prefix="exslt">
 this['node-set'] =  function (x) {
  return x;
  }
</msxsl:script>


<xsl:variable name="x">
  <y/>
</xsl:variable>

<xsl:template match="x">
  <html>
    <head><title>test exslt node set</title></head>
    <body>
      <xsl:apply-templates select="exslt:node-set($x)/*"/>
    </body>
  </html>
</xsl:template>

<xsl:template match="y">
  <p>node set!</p>
</xsl:template>

</xsl:stylesheet>

Это определенно работает в FF, Chrome и IE7+

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