Search code examples
xmlxsltcode-duplication

How to avoid duplication in XSL transformations?


In our application, XML documents of the following structure (much simplified) need to be processed:

<FooBar>
  <Foo ID="attr.1">value1</Foo>
  <Foo ID="attr.2">another value</Foo>
  <Bar ID="attr.3">
    <Foo>1</Foo>
    <Foo>2</Foo>
  </Bar>
</FooBar>

These documents are processed using 2 different transformations. Both transformations mainly target the Foo elements and some supporting information that is not displayed in the example above. The first transformation outputs csv where a Foo element is converted into it's own line. The other transformation just outputs the Foo elements as a comma separated list.

Example output of first transformation is:

attr.1-0;attr.1;value1
attr.2-0;attr.2;another value
attr.3-0;attr.3;1
attr.3-1;attr.3;2

Example output of the second transformation is:

attr.1-0,attr.2-0,attr.3-0,attr.3-1

When comparing both transformations they share about 75% of code. In fact the only difference is the named template that is actually used to output text.

The transformations look like the following:

<xsl:template match="/">
  <!-- output some header information -->
  <xsl:apply-templates select="Foo" />
  <xsl:apply-templates select="Bar" />
</xsl:template>
<xsl:template match="Bar">
  <!-- here is also some stuff -->
  <xsl:apply-templates select="Foo" />
</xsl:template>
<xsl:template match="Foo" >
  <!-- output the text that is special to transformation 1 or 2 -->
</xsl:template>

How can I prevent that much code duplication in both transformations?


Solution

  • The solution is just to reorganize the whole code to some new files.

    The point I was missing is that a stylesheet can be linked to diffent other stylesheets via includes if the linked-to stylesheets define a template with the same name that is called in the linking stylesheet.

    Every transformation now has a root stylesheet. That will include a stylesheet which contains shared templates and a stylesheet that will contain the template that is special to the transformation.

    So this would look like:

    transformation1-root.xsl:

    <xsl:include href="transformation-shared.xsl" />
    <xsl:include href="transformation1-specific.xsl" />
    <xsl:template match="/">
      <xsl:call-template name="templateFooBarRoot" />
    </xsl:template>
    

    transformation2-root.xsl:

    <xsl:include href="transformation-shared.xsl" />
    <xsl:include href="transformation2-specific.xsl" />
    <xsl:template match="/">
      <xsl:call-template name="templateFooBarRoot" />
    </xsl:template>
    

    transformation-shared.xsl:

    <xsl:template name="templateFooBarRoot">
      <!-- output some header information -->
      <xsl:apply-templates select="Foo" />
      <xsl:apply-templates select="Bar" />
    </xsl:template>
    <xsl:template match="Bar">
      <!-- here is also some stuff -->
      <xsl:apply-templates select="Foo" />
    </xsl:template>
    <xsl:template match="Foo" >
      <xsl:call-template name="transformationSpecificTemplate" />
    </xsl:template>
    

    transformation1-specific.xsl:

    <xsl:template name="transformationSpecificTemplate">
    <!-- stuff specific to transformation 1 -->
    </xsl:template>
    

    transformation2-specific.xsl:

    <xsl:template name="transformationSpecificTemplate">
    <!-- stuff specific to transformation 2 -->
    </xsl:template>