Merge functionality of two xsl files into a single file (continued…)

后端 未结 3 764
鱼传尺愫
鱼传尺愫 2021-01-24 11:49

This is in continuation of my question:

Merge functionality of two xsl files into a single file (not a xsl import or include issue)


I have to merge

相关标签:
3条回答
  • 2021-01-24 12:18

    I. Performing a chain of transformations is used quite often in XSLT applications, though doing this entirely in XSLT 1.0 requires the use of the vendor-specific xxx:node-set() function. In XSLT 2.0 no such extension is needed as the infamous RTF datatype is eliminated there.

    Here is an example (too-simple to be meaningful, but illustrating completely how this is done):

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:ext="http://exslt.org/common">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="/">
       <xsl:variable name="vrtfPass1">
        <xsl:apply-templates select="/*/*"/>
       </xsl:variable>
    
       <xsl:variable name="vPass1"
            select="ext:node-set($vrtfPass1)"/>
    
       <xsl:apply-templates mode="pass2"
            select="$vPass1/*"/>
     </xsl:template>
    
     <xsl:template match="num[. mod 2 = 1]">
      <xsl:copy-of select="."/>
     </xsl:template>
    
     <xsl:template match="num" mode="pass2">
      <xsl:copy>
        <xsl:value-of select=". *2"/>
      </xsl:copy>
     </xsl:template>
    </xsl:stylesheet>
    

    when this transformation is applied on the following XML document:

    <nums>
      <num>01</num>
      <num>02</num>
      <num>03</num>
      <num>04</num>
      <num>05</num>
      <num>06</num>
      <num>07</num>
      <num>08</num>
      <num>09</num>
      <num>10</num>
    </nums>
    

    the wanted, correct result is produced:

    <num>2</num>
    <num>6</num>
    <num>10</num>
    <num>14</num>
    <num>18</num>
    

    Explanation:

    1. In the first step the XML document is transformed and the result is defined as the value of the variable $vrtfPass1. This copies only the num elements that have odd value (not even).

    2. The $vrtfPass1 variable, being of type RTF, is not directly usable for XPath expressions so we convert it to a normal tree, using the EXSLT (implemented by most XSLT 1.0 processors) function ext:node-set and defining another variable -- $vPass1 whose value is this tree.

    3. We now perform the second transformation in our chain of transformations -- on the result of the first transformation, that is kept as the value of the variable $vPass1. Not to mess with the first-pass template, we specify that the new processing should be in a named mode, called "pass2". In this mode the value of any num element is multiplied by two.

    See also the answer of Michael Kay to your first question, which also explained this general technique.

    II. XSLT 2.0 solution (no RTFs):

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="/">
      <xsl:variable name="vPass1" >
       <xsl:apply-templates select="/*/*"/>
      </xsl:variable>
       <xsl:apply-templates mode="pass2"
            select="$vPass1/*"/>
     </xsl:template>
    
     <xsl:template match="num[. mod 2 = 1]">
      <xsl:copy-of select="."/>
     </xsl:template>
    
     <xsl:template match="num" mode="pass2">
      <xsl:copy>
        <xsl:value-of select=". *2"/>
      </xsl:copy>
     </xsl:template>
    </xsl:stylesheet>
    

    III. Using the compose() and compose-flist() functions/templates of FXSL

    The FXSL library provides two convenient functions/template that support easy chaining of transformations. The former composes two functions/transformations while the latter composes all functions/transformations that are provided in a sequence.

    Here is a simple, complete code example:

    <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 results are produced:

    Compose:
    (*3).(*2) 3 = 
    18
    
    Multi Compose:
    (*3).(*2).(*3) 2 = 
    36
    
    0 讨论(0)
  • 2021-01-24 12:20

    Pure XSLT 1.0 does not support chaining templates (nor stylesheets as a whole). You can either solve this program outside of XSLT by calling the second xslt template and passing it the output of the first manually, or you can use the fairly pervasive extension function node-set(). MSXML, .NET, EXSL and many other implementations support such a function. The namespace prefix for node-set varies depending on XSLT implementation, but the EXSL prefix is a good bet (and .NET, though undocumented, supports this).

    To use node-set store the result of a template in an xsl:variable or xsl:param and do something like <xsl:apply-templates select="exsl:node-set($myvarname)"/>.

    Finally, you can of course rewrite your two templates to provide the functionality of both in one pass - but in general, this isn't a trivial thing to do.

    0 讨论(0)
  • 2021-01-24 12:30

    I don't understand the problem. The solution I posted in your last question already works.

    For this input:

    <?xml version="1.0" encoding="UTF-8"?>
    <Declaration>
        <Message>
            <Meduim>#+#</Meduim>
            <MessageIdentifier>AA</MessageIdentifier>
            <CommonAccessReference></CommonAccessReference>
        </Message>
        <BeginingOfMessage>
            <MessageCode>ISD</MessageCode>
            <DeclarationCurrency></DeclarationCurrency>
            <MessageFunction>5</MessageFunction>
        </BeginingOfMessage>
    </Declaration>
    

    Output will be:

    <?xml version="1.0" encoding="UTF-8"?>
    <Declaration>
        <Message>
            <Meduim/>
            <MessageIdentifier>AA</MessageIdentifier>
        </Message>
        <BeginingOfMessage>
            <MessageCode>ISD</MessageCode>
            <MessageFunction>5</MessageFunction>
        </BeginingOfMessage>
    </Declaration>
    

    You, in fact, don't need your original stylesheet, because basically you just copy the tree.

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