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