Is there a technique to combine a pipeline of XSL transformations into a single transformation?

后端 未结 2 675
感情败类
感情败类 2021-01-18 17:28

I have written an application which uses a pipeline of 15 XSL stylesheets, and I\'m beginning to work on tuning its performance. It\'s designed to be portable, so that it ca

相关标签:
2条回答
  • 2021-01-18 18:02

    There is a technique that allows independent transformations to be chained together where the output of the k-th transformation is the input of the (k+1)-th transformation.

    Here is a simple example:

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

    when this transformation is applied on the following XML document:

    <doc/>
    

    the wanted result (the first pass affffds the element <one/> as a child of the top element, then the second pass adds another child, , immediately after the element` that was created in the first pass) is produced:

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

    There is a very suitable template/function in FXSL to do this: this is the compose-flist template. It takes as parameters an initial data argument and N functions (templates) and produces the chained composition of these functions/templates.

    Here is the test example from the FXSL library:

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

    when this transformation is applied on any xml document (not used), the wanted, correct result is produced:

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

    Do note: In XSLT 2.0 and later no xxx:node-set() extension is necessary, and any of the chained transformations can be contained in a real function.

    0 讨论(0)
  • 2021-01-18 18:08

    One approach is to use modes http://www.w3.org/TR/xslt#modes but you are right that that requires transforming each step into a variable and to use a node-set extension function to be able to apply the next step to the variable contents.

    0 讨论(0)
提交回复
热议问题