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:
- Static Methods: Call static methods directly by passing parameters into the function.
- Constructors and Instance Methods: Processors like
Saxon allow calling constructors using the
new()function and passing the resulting object reference into subsequent instance methods.
<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.
Approach 1: Passing C# Extension Objects (Recommended)
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
- 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, orXPathNavigator). - Security: Restrict permissions when enabling extension functions or scripts, especially when executing transformations on untrusted user-supplied XSLT stylesheets.
- 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.