hepabolu
hepabolu

Reputation: 1274

apply multiple XSL stylesheets with identical templates

I need to apply several XSL stylesheets to an input document but the stylesheet to be imported have some templates named with identical names.

Here is what I need to do:

<xsl:import href="convert_from_a_to_b.xsl"/>
<xls:import href="convert_from_b_to_c.xsl"/>

<xsl:template match="//root">
  <new_root_element>
    <xsl:apply-templates select="./*"/> <!-- handle some transformations in this stylesheet -->
  </new_root_element>
</xsl:template>

<xsl:template match="content_to_convert"/> <!-- the element and its content to be transformed -->
  <content>
    <xsl:variable name="convertedContent">
      <xsl:next-match/>
      <xsl:apply-templates select="./*"/> <!-- this should be from convert_a_to_b -->
    </xsl:variable>
    <xsl:next-match/>
    <xsl:apply-templates select="$convertedContent"/> <!-- this should be from convert_b_to_c -->
  </content>
</xsl:template>

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

convert_a_to_b.xsl

<xsl:include href="ab_include.xsl"/>

<xsl:template match="/" name="baseContent">
  <xsl:param name="in" select="//SubRoot/root/content_to_convert"/>

  <!-- more processing -->
</xsl:template>
     
</xsl:template>

ab_include.xsl


<xsl:template name="template-1234">
  <xsl:param name="in" select="."/>

  <!-- more processing -->
</xsl:template>

<xsl:template match="subSet">
  <xsl:for-each select="./something">
    <xsl:call-template name="template-1234">
      <xsl:with-param name="in" select="./detail"/>
    <xsl:call-template>
  </xsl:for-each>
</xsl:template>

convert_b_to_c.xsl

<xsl:include href="bc_include.xsl"/>

<xsl:template match="/" name="baseContent">
  <xsl:param name="in" select="//converted_to_a"/>
   
        <xsl:choose>
            <xsl:when test="count($in) gt 1">
                <batch xmlns="">
                    <xsl:for-each select="$in">
                        <xsl:call-template name="subTemplate">
                            <xsl:with-param name="param1" select="value1"/>
                            <xsl:with-param name="param2" select="value2"/>
                        </xsl:call-template>
                    </xsl:for-each>
                </batch>
            </xsl:when>
            <xsl:otherwise>
                xsl:call-template name="subTemplate">
                            <xsl:with-param name="param1" select="$in/value1"/>
                            <xsl:with-param name="param2" select="$in/value2"/>
                        </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
</xsl:template>

<xsl:template name="subTemplate">
  <xsl:param name="param1"/>
  <xsl:param name="param2"/>
  <!-- more processing -->
</xsl:template>
     

bc_include.xsl


<xsl:template name="template-1234">
  <xsl:param name="time" select="."/>

  <!-- more processing -->
</xsl:template>

<xsl:template match="otherSubSet">
  <xsl:for-each select="./somethingElse">
    <xsl:call-template name="template-1234">
      <xsl:with-param name="time" select="./detail"/>
    <xsl:call-template>
  </xsl:for-each>
</xsl:template>

Input_file.xml


<SomeRoot>
  <SubRoot>
    <root>
      <content_to_be_copied_over/> <!-- this I can handle -->
      <content_to_convert>
       <!-- all kinds of data to be converted -->
      </content_to_convert>
    </root>
    <root>
      <content_to_be_copied_over/> <!-- this I can handle -->
      <content_to_convert>
       <!-- all kinds of data to be converted -->
      </content_to_convert>
    </root>
    <root>
      <content_to_be_copied_over/> <!-- this I can handle -->
      <content_to_convert>
       <!-- all kinds of data to be converted -->
      </content_to_convert>
    </root>
  </SubRoot>
</SomeRoot>

Output should be:

<SomeRoot>
  <SubRoot>
    <new_root_element>
      <content>
        <!-- converted content -->
      </content>
    </new_root_element>
    <new_root_element>
      <content>
        <!-- converted content -->
      </content>
    </new_root_element>
    <new_root_element>
      <content>
        <!-- converted content -->
      </content>
    </new_root_element>
  </SubRoot>
</SomeRoot>

However, both stylesheets a lot of includes and have several templates with the same name (e.g. 'template-1234') but different parameters. They have no dependency on each other, but deep down in the included stylesheet stacks they might include the same XSL stylesheet.

The above works if I only import the first stylesheet but after adding the second import I get errors about missing parameters. E.g. I get an error parameter in is not declared in the called template.

I cannot change the stylesheets, because they are produced and maintained by a different group. I can only use them.

Is there a way I can isolate the stylesheets so I can use both in 1 stylesheet and indicate which one I want to use?

NB. I use xslt 3.0 in OxygenXMLEditor with Saxon PE 11.4

Upvotes: 0

Views: 95

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167716

To elaborate from my suggestion in the comment, as you are using XSLT 3 and as you seem to want to chain stylesheets without being able to edit them, it appears one way is to use fn:transform with fold-left e.g. fold-left(('convert_from_a_to_b.xsl', 'convert_from_b_to_c.xsl'), $some-node, function($a, $s) { transform(map{'source-node': $a, 'stylesheet-node': doc($s) })?output }), perhaps, in the context of your example

<xsl:template match="content_to_convert"/> <!-- the element and its content to be transformed -->
  <content>
    <xsl:sequence
      select="fold-left(('convert_from_a_to_b.xsl', 'convert_from_b_to_c.xsl'), ., function($a, $s) { transform(map{'source-node': $a, 'stylesheet-node': doc($s) })?output })"/>
  </content>
</xsl:template>

I can't tell in detail what you are trying to achieve and whether the suggestion helps and works out, to provide a working example see the directory https://github.com/martin-honnen/martin-honnen.github.io/tree/master/xslt/2023/chain-stylesheets-using-fold-left which chains three stylesheets sheet1.xsl, sheet2.xsl, sheet3.xsl by using the suggested fold-left/transform combination from the new main stylesheet fold-left-transform-sample1.xml on the input sample-input1.xml; the stylesheet doing nothing but the identity transformation plus adding a comment that they processed the input, so with the input being e.g.

<root>This is a test sample.</root>

the result of processing the input file with Saxon and the XSLT file fold-left-transform-sample1.xml from https://raw.githubusercontent.com/martin-honnen/martin-honnen.github.io/master/xslt/2023/chain-stylesheets-using-fold-left/fold-left-transform-sample1.xsl is e.g.

<?xml version="1.0" encoding="UTF-8"?>
<root>This is a test sample.</root>
<!--Processed by https://raw.githubusercontent.com/martin-honnen/martin-honnen.github.io/master/xslt/2023/chain-stylesheets-using-fold-left/sheet1.xsl with SAXON HE 11.5 at 2023-02-28T17:23:57.3777158+01:00-->
<!--Processed by https://raw.githubusercontent.com/martin-honnen/martin-honnen.github.io/master/xslt/2023/chain-stylesheets-using-fold-left/sheet2.xsl with SAXON HE 11.5 at 2023-02-28T17:23:57.406232+01:00-->
<!--Processed by https://raw.githubusercontent.com/martin-honnen/martin-honnen.github.io/master/xslt/2023/chain-stylesheets-using-fold-left/sheet3.xsl with SAXON HE 11.5 at 2023-02-28T17:23:57.4349873+01:00-->

The stylesheet does e.g.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  expand-text="yes">
  
  <xsl:param name="xslt-uris" as="xs:string*" select="'sheet1.xsl', 'sheet2.xsl', 'sheet3.xsl'"/>

  <xsl:output indent="yes"/>

  <xsl:template match="/" name="xsl:initial-template">
    <xsl:sequence
      select="fold-left($xslt-uris, /, function($a, $s) { transform(map{'source-node': $a, 'stylesheet-node': doc($s) })?output })"/>
  </xsl:template>
  
</xsl:stylesheet>

Upvotes: 1

Related Questions