XSLT2 translations with Mule2

When you use Mule to integrate several applications that are ‘talking’ xml, sooner or later you will end up translating XML messages from one format to another. This can be done with XSLT that has very powerful abilities. I have already posted about these techniques here and here. Sometimes you just need some more power, so you end up using XSLT2 specific functions. That shouldn’t be a problem as long as you can make sure that the XSLT engine that is used at runtime does understand XSLT2!
Few weeks ago I noticed that the engine that Mule2 uses, Saxon, by default didn’t understand the XSLT2 functions. To show this I have put up the following test case. I have a Mule config file:

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesource.org/schema/mule/core/2.2"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:spring="http://www.springframework.org/schema/beans"
       xmlns:stdio="http://www.mulesource.org/schema/mule/stdio/2.2"
       xmlns:vm="http://www.mulesource.org/schema/mule/vm/2.2"
       xmlns:xm="http://www.mulesource.org/schema/mule/xml/2.2"
       xsi:schemaLocation="
       http://www.mulesource.org/schema/mule/core/2.2 http://www.mulesource.org/schema/mule/core/2.2/mule.xsd
       http://www.mulesource.org/schema/mule/stdio/2.2 http://www.mulesource.org/schema/mule/stdio/2.2/mule-stdio.xsd
       http://www.mulesource.org/schema/mule/vm/2.2 http://www.mulesource.org/schema/mule/vm/2.2/mule-vm.xsd
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.mulesource.org/schema/mule/xml/2.2 http://www.mulesource.org/schema/mule/xml/2.2/mule-xml.xsd">

    <xm:xslt-transformer name="transformClientToCustomer" xsl-file="xslt/client-to-customer.xslt" outputEncoding="UTF-8"/>

    <model name="xslt-mule">
        <service name="xsltService" >
            <inbound>
                <vm:inbound-endpoint path="input" />
            </inbound>
            <outbound>
                <pass-through-router>
                    <stdio:outbound-endpoint system="OUT">
                        <transformer ref="transformClientToCustomer"/>
                    </stdio:outbound-endpoint>
                </pass-through-router>
            </outbound>
        </service>
    </model>
</mule>

It just receives an XML message as String and translates it by using a XSLT Transformer. The Xslt file that is used looks like this (please note that this example is just that, an example , don’t expect anything useful in this xslt file):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:ns0="htpp://www.pascalalma.net/customer">

    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <!-- first copy the root and apply templates-->
    <xsl:template match="/">
        <xsl:copy>
            <!-- copy the attributes (if any) of the root element -->
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <!-- match any other element and copy it with the attributes -->
    <xsl:template match="*">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <!-- Add the necessary namespaces to the root of the schema -->
    <xsl:template match="ns0:customer">
        <!-- Recreate the xs:schema element-->
        <xsl:element name="client">
            <!-- Copy the existing attributes in the original xs:schema element to this one -->
            <xsl:copy-of select="@*"/>
            <!-- add Namespace -->
            <xsl:attribute name="targetNamespace">htpp://www.pascalalma.net/client</xsl:attribute>
            <xsl:attribute name="elementFormDefault">qualified</xsl:attribute>
            <xsl:namespace name="tns">htpp://www.pascalalma.net/client</xsl:namespace>
            <!-- continue with matching al child elements of the xs:schema element-->
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="ns0:customer/ns0:address/ns0:nr">
        <xsl:element name="nr">
            <xsl:value-of select="xs:nonNegativeInteger(50)"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

The test class to test the config file looks like:

package net.pascalalma.mule;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.mule.api.MuleMessage;
import org.mule.module.client.MuleClient;
import org.mule.tck.FunctionalTestCase;

/**
 * Unit test for simple App.
 */
public class MyTestCase extends FunctionalTestCase {

    private MuleClient client = null;

    public void testXslt() throws Exception {
        MuleMessage msg = getMuleClient().send("vm://input", getFileAsString("xml/customer.xml"), null);
    }

    protected String getConfigResources() {
        return "config/mule-config.xml";
    }

    private MuleClient getMuleClient() throws Exception {
        if (client == null) {
            client = new MuleClient();
        }

        return client;
    }

    public static String getFileAsString(String url) {
        return convertStreamToString(MyTestCase.class.getClassLoader().getResourceAsStream(url));
    }

    /**
     * Convert InputStream to String.
     * @param is
     * @return String representation of incoming stream.
     */
    private static String convertStreamToString(InputStream is) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();

        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }
}

When you run this test class with this xml as input:

<?xml version="1.0" encoding="UTF-8"?>
<ns0:customer xmlns:ns0="htpp://www.redstream.nl/customer">
    <ns0:firstName>Pascal</ns0:firstName>
    <ns0:lastName>Alma</ns0:lastName>
    <ns0:address>
        <ns0:street>sesamestreet</ns0:street>
        <ns0:nr>13</ns0:nr>
        <ns0:postalCode>
            <ns0:firstPart>1234</ns0:firstPart>
            <ns0:secondPart>AB</ns0:secondPart>
        </ns0:postalCode>
    </ns0:address>
</ns0:customer>

you will receive an error message:

Error at xsl:value-of on line 39 of :
XPST0080: XPath syntax error at char 25 on line 39 in {xs:nonNegativeInteger(50)}:
The type xs:nonNegativeInteger is not recognized by a Basic XSLT Processor.

So the problem is clear, I think. To solve this you just need to add the following to the root element of your xslt file:

<xsl:stylesheet version="2.0
...
xmlns:saxon="http://saxon.sf.net/" saxon:allow-all-built-in-types="yes"
...>
</pre>
Now when you run the test class you will get the expected result:
<pre lang='xml'>
<?xml version="1.0" encoding="UTF-8"?>
<client xmlns:tns="htpp://www.pascalalma.net/client"
        targetNamespace="htpp://www.pascalalma.net/client"
        elementFormDefault="qualified">
    <ns0:firstName xmlns:ns0="htpp://www.pascalalma.net/customer">Pascal</ns0:firstName>
    <ns0:lastName xmlns:ns0="htpp://www.pascalalma.net/customer">Alma</ns0:lastName>
    <ns0:address xmlns:ns0="htpp://www.pascalalma.net/customer">
        <ns0:street>sesamestreet</ns0:street>
        <nr>50</nr>
        <ns0:postalCode>
            <ns0:firstPart>1234</ns0:firstPart>
            <ns0:secondPart>AB</ns0:secondPart>
        </ns0:postalCode>
    </ns0:address>
</client>

About Pascal Alma

Pascal is a senior IT consultant and has been working in IT since 1997. He is monitoring the latest development in new technologies (Mobile, Cloud, Big Data) closely and particularly interested in Java open source tool stacks, cloud related technologies like AWS and mobile development like building iOS apps with Swift. Specialties: Java/JEE/Spring Amazon AWS API/REST Big Data Continuous Delivery Swift/iOS
This entry was posted in Mule, XML/ XSD/ XSLT and tagged , . Bookmark the permalink.