Skip to Content
Technical Articles
Author's profile photo Morten Wittrock

XSLT stylesheet inheritance with xsl:import

Extensible Stylesheet Language Transformations, or XSLT for short, is a powerful language for writing XML transformations. With XSLT, you can create succint and elegant solutions to thorny XML mapping problems. The language has a number of cool tricks up its sleeve, and in this blog post, I’m going to show you one of the lesser known ones: Stylesheet inheritance with the <xsl:import> declaration.

With this technique, you can solve mapping problems that would otherwise require you to make a copy of an existing stylesheet, and modify the copy. In other words: With stylesheet inheritance, you can uphold the Don’t Repeat Yourself (DRY) principle and avoid code duplication.

To start things off, let’s take a look at how you can include an XSLT stylesheet in another stylesheet.

Importing stylesheets

XSLT supports two ways of including a stylesheet in another stylesheet: <xsl:include> and <xsl:import>. Both declarations must be top-level elements in the stylesheet, i.e. children of the <xsl:stylesheet> element. However, <xsl:import> elements must always be the first child elements of <xsl:stylesheet>.

<xsl:include> and <xsl:import> are similar, but they differ in one area, which is central to the topic of this blog post: When importing a stylesheet with <xsl:import>, templates defined in the importing stylesheet, has higher precedence than templates defined in the imported stylesheet.

What that means, is that a stylesheet that imports another stylesheet, can override the templates defined in the imported stylesheet. This lets us create a customised version of a stylesheet, changing only certain aspects of that stylesheet.

A concrete example

Let’s get a little more practical, and take a look at a concrete example. Here’s a simple stylesheet that creates a header, a footer and five <line> elements in between:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    
    <xsl:template match="/">
        <doc>
            <xsl:call-template name="header"/>
            <xsl:call-template name="lines"/>
            <xsl:call-template name="footer"/>
        </doc>
    </xsl:template>
    
    <xsl:template name="header">
        <header>
            <xsl:text>Header content goes here</xsl:text>
        </header>
    </xsl:template>

    <xsl:template name="lines">
        <lines>
            <xsl:for-each select="1 to 5">
                <xsl:variable name="linenum" select="."/>
                <xsl:call-template name="single-line">
                    <xsl:with-param name="linenum" select="$linenum"/>
                    <xsl:with-param name="content" select="concat('Line ', $linenum, ' content goes here')"/>
                </xsl:call-template>
            </xsl:for-each>
        </lines>
    </xsl:template>
    
    <xsl:template name="single-line">
        <xsl:param name="linenum"/>
        <xsl:param name="content"/>
        <line number="{$linenum}">
            <xsl:value-of select="$content"/>
        </line>
    </xsl:template>

    <xsl:template name="footer">
        <footer>
            <xsl:text>Footer content goes here</xsl:text>
        </footer>
    </xsl:template>
    
</xsl:stylesheet>

It generates the following output:

<?xml version="1.0" encoding="UTF-8"?>
<doc>
    <header>Header content goes here</header>
    <lines>
        <line number="1">Line 1 content goes here</line>
        <line number="2">Line 2 content goes here</line>
        <line number="3">Line 3 content goes here</line>
        <line number="4">Line 4 content goes here</line>
        <line number="5">Line 5 content goes here</line>
    </lines>
    <footer>Footer content goes here</footer>
</doc>

(For simplicity’s sake, this stylesheet always produces the same output, regardless of which document it is applied to.)

Now imagine that we want to make a slight change. In another integration scenario, we need the <line> element to look like this:

<line>
    <number>x</number>
    <content>Line x content goes here</content>
</line>

How can we achieve this? Well, we could make a copy of the original stylesheet, and then rewrite the single-line template to produce the desired output. That would work, obviously, but it’s not the best solution by far. All updates and fixes made to the original stylesheet would have to be applied to the copy as well. DRY, right?

Instead, let’s use the <xsl:import> declaration to import the stylesheet general.xsl into a second, smaller stylesheet, and then override the single-line template. Here’s the complete stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    
    <xsl:import href="general.xsl"/>
    
    <xsl:template name="single-line">
        <xsl:param name="linenum"/>
        <xsl:param name="content"/>
        <line>
            <number>
                <xsl:value-of select="$linenum"/>
            </number>
            <content>
                <xsl:value-of select="$content"/>    
            </content>
        </line>
    </xsl:template>
    
</xsl:stylesheet>

This customised stylesheet produces the following output:

<?xml version="1.0" encoding="UTF-8"?>
<doc>
    <header>Header content goes here</header>
    <lines>
        <line>
            <number>1</number>
            <content>Line 1 content goes here</content>
        </line>
        <line>
            <number>2</number>
            <content>Line 2 content goes here</content>
        </line>
        <line>
            <number>3</number>
            <content>Line 3 content goes here</content>
        </line>
        <line>
            <number>4</number>
            <content>Line 4 content goes here</content>
        </line>
        <line>
            <number>5</number>
            <content>Line 5 content goes here</content>
        </line>
    </lines>
    <footer>Footer content goes here</footer>
</doc>

The <line> element has been updated according to the new requirement, but the rest of the output remains unchanged. Mission accomplished, without duplicated code!

Stylesheet inheritance in Cloud Integration

There’s not much to implementing the technique discussed here in Cloud Integration. Upload both stylesheets on the Resources tab of your integration flow, and add an XSLT Mapping step that uses the importing stylesheet. That’s it; CPI’s XSLT processor will take it from there.

Assigned Tags

      7 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Olegs Veliks
      Olegs Veliks

      Hello Morten.

       

      I have generic question - what are those requirements (based on your experience) when we should give preference to XSLT, instead of regular mapping?

      Did you faced any tasks when XML transformation can be solved only using XSLT? Thanks.

      Author's profile photo Morten Wittrock
      Morten Wittrock
      Blog Post Author

      Hi Olegs

      That’s a very good question. I should probably expand on this in a separate blog post, but let me give you a brief answer now. XSLT is particularly well suited for complex mappings, where the source and target structures are very different. XPath is at the core of XSLT, so compared to Message mapping, navigating XML structures, and implementing complex rules is incredibly easy. Message Mapping is easy to pick up and get started with, and for mappings that are not too complicated, it’s a very nice tool. However, complicated mappings can be really hard to implement in Message Mapping, and at the same time almost trivial in XSLT. That power comes at the cost of a quite steep learning curve, though.

      Regards,

      Morten

       

      Author's profile photo Morten Wittrock
      Morten Wittrock
      Blog Post Author

      Hi Olegs

      I finally got around to writing a blog post comparing the mapping options offered by Cloud Integration. You can check it out here: Cloud Integration mapping: Your options explained and compared

      Regards,

      Morten

      Author's profile photo Florian Kube
      Florian Kube

      Thank you Morten.

      It would be nice if it would be possible to add a folder or archive on the CPI. So it would be possible to organize the sub XSLT scripts.

      Author's profile photo Morten Wittrock
      Morten Wittrock
      Blog Post Author

      Hi Florian

      The resource handling in WebUI has improved quite a lot over time, so I would definitely not rule something like that out. Let's see what happens 🙂

      Regards,

      Morten

       

      Author's profile photo Jürgen Döbbeler
      Jürgen Döbbeler

      Hi all,

       

      I know the blog is already a little bit older, but I just tried it and it is not successful. Can anybody confirm that the inheritance is still working in CPI for XSLT resources?

      Thanks and Regards

       

      Jürgen

      Author's profile photo Morten Wittrock
      Morten Wittrock
      Blog Post Author

      Hi Jürgen

      I just tried the example, and it still works as intended.

      How does it fail for you? Also, are you on Neo or Cloud Foundry?

      Regards,

      Morten