I'm trying to learn the different possibilities to generalize XSLT templates for the sake of reusing them at different places. So far, I have two cases where I do not know how to proceed.
Case 1 - source XML might contain nodes Foo1, Foo2, ..., Foo10
(but doesn't have to contain any or all of them). For example,
<Foo1>some value</Foo1>
<Foo3>some other value</Foo3>
I need to create nodes as follows:
<Bar number="1">some value</Bar>
<Bar number="3">some other value</Bar>
My XSLT currently is very simple:
<xsl:if test="Foo1 != ''">
<xsl:element name="Bar">
<xsl:attribute name="number">1</xsl:attribute>
<xsl:value-of select="Foo1"/>
</xsl:element>
</xsl:if>
But I obviously need 10 of these code blocks. How can I generalize this?
Case 2 - In the source XML, I have a couple of nodes of basically the same structure:
<Foo>
<item>
<Start>2015-06-01</Start>
<End>9999-12-31</End>
<Foo>00000008</Foo>
</item> <!-- 0..n items -->
</Foo>
The nodes differ in the name Foo
, but the rest stays the same. The structure I need to build looks like this:
<Bars>
<Bar From="2015-06-01" To="9999-12-31">
<Value>00000008</Value>
</Bar>
</Bars>
here's my XSLT attempt, but once again, I need many templates which are very similar to each other:
<xsl:element name="Bars>
<apply-templates select="Foo"/>
</xsl:element>
...
<xsl:template match="Foo/item">
<xsl:element name="Bar">
<xsl:attribute name="From">
<xsl:call-template name="convertDate">
<xsl:with-param name="theDate" select="Start"/>
</xsl:call-template>
</xsl:attribute>
<xsl:attribute name="To">
<xsl:call-template name="convertDate">
<xsl:with-param name="theDate" select="End"/>
</xsl:call-template>
</xsl:attribute>
<xsl:element name="Value">
<xsl:value-of select="Foo"/>
</xsl:element>
</xsl:element>
</xsl:template>
And once more, I have several of the templates, all of which look very similar (i.e., they only differ in the names of the Foo
, Bar
, and Value
elements). Any chance to generalize this, i.e. to provide a single template which can handle all these cases?
You can use
<xsl:template match="*[starts-with(local-name(), 'Foo')]">
<Bar number="{translate(local-name(), translate(local-name(), '1234567890', ''), '')}">
<xsl:apply-templates/>
</Bar>
</xsl:template>
for the first sample.
To convert
<item>
<Start>2015-06-01</Start>
<End>9999-12-31</End>
<Foo>00000008</Foo>
</item>
to
<Bar From="2015-06-01" To="9999-12-31">
<Value>00000008</Value>
</Bar>
you can use
<xsl:template match="item">
<Bar From="{Start}" To="{End}">
<xsl:value-of select="Foo"/>
</Bar>
</xsl:template>