是否有将XSL转换管道组合成单个转换的技术?

时间:2010-11-21 11:10:57

标签: xslt transformation pipeline

我编写了一个使用15个XSL样式表管道的应用程序,我开始着手调整其性能。它设计为可移植的,因此可以在Web浏览器环境和桌面上运行。在桌面上,我认为将样式表分离出来作为多个转换的管道可能是有意义的,因为这允许每个单独的转换在其自己的线程中运行,这在具有多个核的CPU上非常有效。但是,不仅浏览器环境是单线程的,在大多数浏览器中,暴露给JavaScript的XSL处理API需要将每个单独转换的结果解析回DOM对象,这似乎效率低下。因此,如果可能的话,在浏览器环境的上下文中运行时,将所有样式表组合成单个样式表是有利的。我知道如何使用exsl:node-set(大多数浏览器支持)来实现这一点,但是我不清楚我想象的技术是否可以推广。是否有一种将XSL样式表管道转换为单个XSL样式表的通用技术,以便保留整个管道的语义?自动化解决方案将是理想的。

2 个答案:

答案 0 :(得分:2)

有一种技术允许将独立变换链接在一起,其中第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/>添加为top元素的子元素,然后第二遍传递另一个子元素, 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()扩展名,并且任何链式转换都可以包含在实际函数中。

答案 1 :(得分:0)

一种方法是使用模式http://www.w3.org/TR/xslt#modes但你是对的,这需要将每个步骤转换为变量并使用节点集扩展函数来将下一步应用于变量内容。 / p>