Есть ли способ объединить конвейер преобразований XSL в одно преобразование? - PullRequest
5 голосов
/ 21 ноября 2010

Я написал приложение, которое использует конвейер из 15 таблиц стилей XSL, и я начинаю работать над настройкой его производительности. Он разработан для переносимости, поэтому его можно запускать как в среде веб-браузера, так и на рабочем столе. На рабочем столе, я думаю, может иметь смысл разделять таблицы стилей как конвейер из нескольких преобразований, поскольку это позволяет каждому отдельному преобразованию выполняться в своем собственном потоке, что может быть очень эффективным на процессорах с несколькими ядрами. Однако не только среда браузера является однопоточной, в большинстве браузеров API обработки XSL, работающий с JavaScript, требует синтаксического анализа результата каждого отдельного преобразования обратно в объект DOM, что кажется неэффективным. Поэтому я думаю, что было бы выгодно объединить все таблицы стилей в одну таблицу стилей при запуске в контексте среды браузера, если это возможно. У меня есть идея, как это можно сделать с помощью exsl: node-set (который поддерживается большинством браузеров), но мне не ясно, является ли техника, которую я представляю, обобщаемой. Существует ли общая методика преобразования конвейера таблиц стилей XSL в одну таблицу стилей XSL, чтобы семантика всего конвейера сохранялась? Автоматизированное решение было бы идеально.

Ответы [ 2 ]

2 голосов
/ 21 ноября 2010

Существует метод , который позволяет объединять независимые преобразования, когда выход k-го преобразования является входом (k + 1) -го преобразования.

Вот простой пример :

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ext="http://exslt.org/common"
    exclude-result-prefixes="ext xsl">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="node()|@*" name="identity">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
   <xsl:apply-templates select="node()"/>
  </xsl:variable>

  <xsl:apply-templates mode="pass2"
   select="ext:node-set($vrtfPass1)/node()"/>
 </xsl:template>

 <xsl:template match="/*">
     <xsl:copy>
       <xsl:copy-of select="@*"/>
       <one/>
     <xsl:apply-templates/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="node()|@*" mode="pass2">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*" mode="pass2"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="/*/one" mode="pass2" >
     <xsl:call-template name="identity"/>
      <two/>
 </xsl:template>
</xsl:stylesheet>

, когда это преобразование применяется к следующему документу XML:

<doc/>

требуемый результат (первый проход добавляет элемент <one/> в качестве дочернего элемента верхнего элемента, затем второй проход добавляет другого дочернего элемента, , immediately after the element `, который был создан в первом проходе) создается :

<doc>
   <one/>
   <two/>
</doc>

В FXSL есть очень подходящий шаблон / функция для этого : это compose-flistшаблон.Он принимает в качестве параметров исходный аргумент данных и N функций (шаблонов) и создает цепочечный состав этих функций / шаблонов.

Вот пример теста из библиотеки FXSL :

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:myFun1="f:myFun1"
xmlns:myFun2="f:myFun2" 
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="xsl f ext myFun1 myFun2"
>
  <xsl:import href="compose.xsl"/>
  <xsl:import href="compose-flist.xsl"/>

  <!-- to be applied on any xml source -->

  <xsl:output method="text"/>
  <myFun1:myFun1/>
  <myFun2:myFun2/>


  <xsl:template match="/">

    <xsl:variable name="vFun1" select="document('')/*/myFun1:*[1]"/>
    <xsl:variable name="vFun2" select="document('')/*/myFun2:*[1]"/>
    Compose:
    (*3).(*2) 3 = 
    <xsl:call-template name="compose">
      <xsl:with-param name="pFun1" select="$vFun1"/>
      <xsl:with-param name="pFun2" select="$vFun2"/>
      <xsl:with-param name="pArg1" select="3"/>
    </xsl:call-template>

    <xsl:variable name="vrtfParam">
      <xsl:copy-of select="$vFun1"/>
      <xsl:copy-of select="$vFun2"/>
      <xsl:copy-of select="$vFun1"/>
    </xsl:variable>

    Multi Compose:
    (*3).(*2).(*3) 2 = 
    <xsl:call-template name="compose-flist">
      <xsl:with-param name="pFunList" select="ext:node-set($vrtfParam)/*"/>
      <xsl:with-param name="pArg1" select="2"/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="myFun1:*" mode="f:FXSL">
    <xsl:param name="pArg1"/>

    <xsl:value-of select="3 * $pArg1"/>
  </xsl:template>

  <xsl:template match="myFun2:*" mode="f:FXSL">
    <xsl:param name="pArg1"/>

    <xsl:value-of select="2 * $pArg1"/>
  </xsl:template>
</xsl:stylesheet>

когда это преобразование применяется к любому XML-документу (не используется), получается требуемый, правильный результат :

Compose:
(*3).(*2) 3 = 
18

Multi Compose:
(*3).(*2).(*3) 2 = 
36

Примечание : В XSLT 2.0 и более поздних версиях расширение xxx:node-set() не требуется, и любое из связанных преобразований может содержаться в реальной функции.

0 голосов
/ 21 ноября 2010

Один из подходов состоит в том, чтобы использовать режимы http://www.w3.org/TR/xslt#modes, но вы правы, что для этого требуется преобразовать каждый шаг в переменную и использовать функцию расширения набора узлов, чтобы иметь возможность применить следующий шаг к содержимому переменной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...