XSLT 2.0 and Java Extensions

We use a fair lot of XSLT transformations in our projects. Since making XSLT stylesheets is a tedious and error-prone job, we use Altova MapForce to do the job. MapForce is very versatile, but can not always cope with the requirements our customers come up with. This time, the customer asked us to implement some sort of maintainable translation mechanism. This mechanism should be able to translate incoming values using a user maintainable collection of values. This way, the transformation process does not need to be changed if a translation is added or updated.

Adding a (hardcoded) parameterized XSLT template is not an option, because this is not maintainable without changing the stylesheet. The answer? XSLT Java extensions!

In this blog I’ll show you a simple example on how to use Java extensions in a XSLT 2.0 stylesheet.

We’ll start with a very simple “Hello World!”-isch example. I’ve created the following Java class which we will be using in our stylesheet:

Our custom Java class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package nl.redstream.utils;
 
import java.io.IOException;
import java.util.Properties;
 
public class XsltUtils {
 
    /**
     * Your default Hello World! method.
     */
    public static String greeting(String name) {
        return "Hello, " + name + " !";
    }
 
}

Notice that the greeting method is public static (not really needed, but makes things easier) .

I packaged the project into a single jar (let’s assume it is called: redstream-xslt-demo-1.0-SNAPSHOT.jar) using mvn clean package.

The XSL stylesheet

Next up, creating our stylesheet ! (We will be dealing with the jar later) Use the following stylesheet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:fn="http://www.w3.org/2005/xpath-functions"
    xmlns:demo="java:nl.redstream.utils.XsltUtils" 
    exclude-result-prefixes="fn demo">
  <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
  <xsl:template match="/">
    <dataset>
      <xsl:for-each select="dataset">
        <row>
          <xsl:for-each select="row/column">
            <column>
              <xsl:attribute name="name" select="@name"/>
              <xsl:attribute name="value" select="demo:greeting(fn:string(@value))"/>
            </column>
          </xsl:for-each>
        </row>
      </xsl:for-each>
    </dataset>
</xsl:template>
</xsl:stylesheet>

Notice two things:
- Line 5 contains a declaration of our custom namespace. Just java: and the FQN of your custom Java class. The prerequisite is that the class (optionally packaged in a jar) is on the classpath.
- Line 15 contains a reference to our Java function. Notice that you can use an Xpath expression which will be resolved at runtime and passed as an argument to the greeting method.

Disclaimer

Remember the declaration of the namespace? This is dependent of the used XSLT engine. When using Saxon, you have to use the java:FQN declaration. When you are using Xalan, for example, you use xalan://FQN. Please check the documentation of your selected XSLT engine, before you declare the namespace.

XMLSpy

We could perform the XSL transformation using the command line, but we are using Altova XMLSpy for this. By default, XMLSpy uses a built-in XSLT engine, but this can be changed to a custom one. Goto: “Tools -> Options: External XSL transformation program” and copy-paste the following: java -cp C:\develop\redstream-xslt-demo-1.0-SNAPSHOT.jar;C:\develop\saxon8.jar net.sf.saxon.Transform -o %2 %1 %3.

We will be using Saxon as XSLT engine, because the endresult will be packaged in a Mule ESB application … and Mule uses Saxon (so, that’s why). Should you want to use another XSLT engine, customize the ‘External XSL transformation program’ in XMLSpy accordingly. Notice that all necessary jar’s are copied to the c:\develop directory and included in the classpath (including our custom jar).

Assign the following XML to the XSL using XSL/XQuery -> Assign sample XML file:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
  <row>
    <column name="greeting" value="foo"/>
    <column name="another" value="bar"/>
  </row>
</dataset>

After pressing F10 the following output should be the result of the transformation:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
   <row>
      <column name="greeting" value="Hello, foo !"/>
      <column name="another" value="Hello, bar !"/>
   </row>
</dataset>

What’s happened is that values foo and bar were passed to the Java greeting method and returned the displayed string. Of course this is a simplified example, but you’ll catch the drift. This opens up possibilites: you now can breakout of the XSLT 2.0 barriers and use Java where needed. Use your imagination to create your own custom Java class.

That’s it … we’ve shown that you can use custom Java classes in your XSLT 2.0 transformation. In my next post, I’ll show you how to extend Altova MapForce and make this reusable (read: drag-and-drop the Java methods graphically to auto generate the XSLT 2.0 stylesheet).

categories: MapForce, Mule, XML/ XSD/ XSLT

About Pascal Prins

Started in 1996. Eye for detail and environment. Broad experience, mainly covering integration related technologies, tools and solutions. Involved in many projects as technical lead, architect or business consultant. One wife, two beautiful daughters, one magnificent son ... and one cat.

Comments are closed.