How to Call Java and C# Extensions in XSLT

Extensible Stylesheet Language Transformations (XSLT) provides robust tools for transforming XML documents, but complex requirements like external API calls, cryptographic hashing, or advanced calculations often exceed standard XPath capabilities. To handle these scenarios, XSLT processors allow developers to invoke custom code written in languages such as Java or C#. This guide demonstrates how to declare, configure, and invoke external Java and C# extension functions from within an XSLT stylesheet.


Invoking Java Extensions in XSLT

Java extensions are commonly used with processors such as Saxon or Apache Xalan. The processor binds an XML namespace URI to a Java class, allowing you to invoke static or instance methods directly through XPath expressions.

1. Declare the Java Namespace

To map a Java class, define a namespace in the <xsl:stylesheet> element using the java: prefix followed by the fully qualified class name.

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:math="java:java.lang.Math"
    xmlns:custom="java:com.example.FormatHelper"
    exclude-result-prefixes="math custom">

2. Call Java Methods in XPath

Once mapped, invoke the Java methods using the declared namespace prefix:

<xsl:template match="/data">
    <output>
        <!-- Calling a built-in static method: Math.max() -->
        <max-value>
            <xsl:value-of select="math:max(number(val1), number(val2))" />
        </max-value>

        <!-- Calling a custom static method: FormatHelper.formatDate() -->
        <formatted-date>
            <xsl:value-of select="custom:formatDate(string(rawDate))" />
        </formatted-date>
    </output>
</xsl:template>

Invoking C# Extensions in .NET XSLT

In the .NET ecosystem using XslCompiledTransform, C# extensions can be invoked using two primary approaches: passing an external extension object via code, or embedding C# code directly into the XSLT using msxsl:script.

Passing extension objects via XsltArgumentList keeps business logic decoupled from the stylesheet and avoids security issues associated with dynamic script compilation.

Step 1: Create the C# Class

namespace MyNamespace
{
    public class TextUtility
    {
        public string ToUpper(string input)
        {
            return input?.ToUpperInvariant();
        }
    }
}

Step 2: Reference the Namespace in XSLT

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:util="urn:my-custom-utility"
    exclude-result-prefixes="util">

    <xsl:template match="/user">
        <name>
            <xsl:value-of select="util:ToUpper(string(name))" />
        </name>
    </xsl:template>
</xsl:stylesheet>

Step 3: Bind and Execute in C

using System.IO;
using System.Xml.Xsl;

var transform = new XslCompiledTransform();
transform.Load("transform.xslt");

var args = new XsltArgumentList();
args.AddExtensionObject("urn:my-custom-utility", new MyNamespace.TextUtility());

using (var writer = new StreamWriter("output.xml"))
{
    transform.Transform("input.xml", args, writer);
}

Approach 2: Embedded C# Scripts (msxsl:script)

You can embed C# code directly within the XSLT file using Microsoft’s msxsl:script element.

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:cs="urn:custom-csharp-code"
    exclude-result-prefixes="msxsl cs">

    <msxsl:script language="C#" implements-prefix="cs">
        <![CDATA[
        public string ReverseText(string text)
        {
            char[] arr = text.ToCharArray();
            System.Array.Reverse(arr);
            return new string(arr);
        }
        ]]>
    </msxsl:script>

    <xsl:template match="/item">
        <reversed>
            <xsl:value-of select="cs:ReverseText(string(title))" />
        </reversed>
    </xsl:template>
</xsl:stylesheet>

Note: When using msxsl:script, enable script execution in your .NET transform settings:

var settings = new XsltSettings { EnableScript = true };
transform.Load("transform.xslt", settings, null);

Best Practices and Considerations

  1. Type Mapping: XSLT data types (nodesets, strings, numbers, booleans) map directly to native types. Ensure method signatures use corresponding language types (such as string, double, or XPathNavigator).
  2. Security: Restrict permissions when enabling extension functions or scripts, especially when executing transformations on untrusted user-supplied XSLT stylesheets.
  3. Portability: Relying on custom Java or C# extensions ties the stylesheet to a specific runtime engine. If multi-platform portability is required, consider using standard XSLT 2.0/3.0 functions where possible.