2010-11-21 39 views
4

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

+0

好问题,+1。请参阅我的答案,以获得广泛的解释以及提出的解决方案的两个示例。 – 2010-11-21 17:16:03

回答

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/> 

有用结果(第一遍addds元件<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()扩展名,并且任何链ed变换可以包含在一个实际函数中。

+0

非常彻底。感谢您的答复! – jbeard4 2010-11-21 17:56:45

0

一种方法是使用http://www.w3.org/TR/xslt#modes模式,但是您需要将每个步骤转换为变量并使用节点集扩展功能才能将下一步应用于变量内容。