Custom functions
Extend Art2link ESB with reusable C# functions — callable from maps, port and adapter properties, pipeline component configuration, and routing expressions.
A Custom Function is a C# method that becomes callable from anywhere the Art2link ESB platform accepts a {{Function.fnX}} binding token — inside maps, on port and adapter properties, in pipeline component configuration, and in routing and subscription expressions. Inside Maps, custom functions can also be called natively from XSLT via art:invoke.
Typical use cases include database lookups, external API calls, custom string manipulation, cryptographic helpers, and any business rule that is easier to express in C# than in XSLT or XPath. The built-in XSLT 3.0 / XPath 3.1 function library is comprehensive — reach for a custom function when it isn't.
Custom Functions are written and edited in the Art2link ESB UI, compiled and deployed automatically when you save — no build steps, no restarts. Once deployed, they can be referenced from any map, port, pipeline, or expression in the system.
1. Create a new Custom Function — start from the template provided in the Art2link ESB UI.
2. Write your function — add a public static method to the static class and decorate it with the [CustomFunction("fnName")] attribute.
3. Save — Art2link ESB compiles and deploys the function automatically.
4. Reference it — invoke the function from any binding-aware location: an XSLT map, a port or adapter property, a pipeline component setting, or a routing expression.
| Rule | Details |
|---|---|
| Static class | The class must be declared static. |
| Namespace | Custom.Functions is required. The class name is yours to choose and serves as a logical grouping for your functions. |
| One class per file | Each file must contain exactly one class. |
| Methods | Each function must be public static. Async methods are not supported. |
| Rule | Details |
|---|---|
| Namespace | CC.Art2link.Attributes |
| Parameter | The function name used to invoke the method from any binding-aware location. Must follow standard C# method naming rules (no spaces, no special characters). |
| Uniqueness | Must be unique system-wide across all custom functions in the Art2link ESB instance. |
| Convention | The fn prefix (e.g., fnLookupCustomer) is recommended but not required. |
Both parameters and return types support: string, int, bool, double, decimal, and collections. Multiple parameters are supported.
using CC.Art2link.Attributes; namespace Custom.Functions; public static class MyFunctions { [CustomFunction("fnMyFunction")] public static string MyFunction(string input) { // TODO: Implement your logic here. return input; } }
using CC.Art2link.Attributes; namespace Custom.Functions; public static class StringFunctions { [CustomFunction("fnToTitleCase")] public static string ToTitleCase(string input) { if (string.IsNullOrWhiteSpace(input)) return input; return System.Globalization.CultureInfo.CurrentCulture .TextInfo.ToTitleCase(input.ToLower()); } [CustomFunction("fnParseBool")] public static bool ParseBool(string input) { return input?.Trim().ToLower() is "1" or "true" or "yes" or "y"; } }
Unhandled exceptions thrown by a custom function will surface as errors at the call site — a map execution error, a port-property resolution error, a pipeline component error, or a routing-expression failure, depending on where the function was invoked. Implement exception handling within your functions and either return a meaningful fallback value or rethrow with additional context.
[CustomFunction("fnSafeParseDate")] public static string SafeParseDate(string input, string format) { try { var date = DateTime.ParseExact(input, format, CultureInfo.InvariantCulture); return date.ToString("yyyy-MM-dd"); } catch (FormatException ex) { throw new InvalidOperationException( $"fnSafeParseDate: could not parse '{input}' with format '{format}'", ex); } }
Once a custom function is saved and compiled, it can be called from anywhere the platform resolves binding tokens, and from inside an XSLT map natively. Every call site references the function by its [CustomFunction] attribute name — no class or namespace needed.
| Call site | How to call |
|---|---|
| Maps | Binding token {{Function.fnX(…)}} or native XSLT call art:invoke('fnX', …). |
| Port & adapter properties | Binding token in any property value field on a receive, send, loopback, or null port — including adapter-specific settings. |
| Pipeline component configuration | Binding token in any component property value, resolved at runtime before the component executes. |
| Routing & subscription expressions | Used as the predicate or value in a message type subscription expression. Returns are coerced as needed. |
The two invocation styles below apply specifically inside maps; the other call sites accept the binding-token form only.
The same double-curly binding used for variables, promoted properties, and other map references. After typing {{, intellisense exposes the available custom functions alongside the other binding sources. Calls written this way are resolved by a preprocessor pass before the XSLT runs, so the function executes outside the XSLT context and no XPath context is shared with it.
{{Function.fnFunctionName(parameter)}}
For example, to call fnParseBool:
{{Function.fnParseBool(parameter)}}
Custom functions can also be called inline from XSLT as native functions. This requires declaring the urn:art2link:functions namespace on the stylesheet and excluding its prefix from the result tree:
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:art="urn:art2link:functions"
exclude-result-prefixes="xs art">
With the namespace declared, invoke any custom function via art:invoke, passing the function name as the first argument and the function's parameters after it:
art:invoke('fnFormatDateTime', string(CreatedAt))
Parameters follow standard XPath nomenclature, so any XPath expression — field references, casts, computed values — can be passed in. The function executes and its return value is used inline in the map.
A common use case for custom functions is performing database lookups during map execution — for example, enriching a message with customer data from a SQL database.
The connection string is passed as a parameter to the function, allowing you to configure it in the Art2link ESB UI without hardcoding it in your code.
using CC.Art2link.Attributes; using System.Data; using Microsoft.Data.SqlClient; namespace Custom.Functions; public static class DatabaseLookup { [CustomFunction("fnLookupCustomerName")] public static string LookupCustomerName( string customerId, string connectionString) { try { using var conn = new SqlConnection(connectionString); conn.Open(); using var cmd = conn.CreateCommand(); cmd.CommandText = "SELECT Name FROM Customers WHERE Id = @Id"; cmd.Parameters.AddWithValue("@Id", customerId); var result = cmd.ExecuteScalar(); return result?.ToString() ?? "Unknown"; } catch (Exception ex) { throw new InvalidOperationException( $"fnLookupCustomerName failed for ID '{customerId}': {ex.Message}", ex); } } }
| Aspect | Details |
|---|---|
| Target framework | .NET 8 |
| Mapping engine | SaxonHE 12.9 (XSLT 3.0, XPath 3.1, XQuery 3.1) |
| Available libraries | Built-in .NET libraries. Additional NuGet packages can be added via the Art2link ESB UI (name and version), then referenced with using statements at the top of your function code. See the NuGet packages article for the global catalogue. |
| Async | Not supported. All custom function methods must be synchronous. |
| External resources | HTTP calls, database queries, and file system access are permitted. |
| Logging | No logging interface is available at this time. |
| One class per file | Each file must contain exactly one class. A class may contain multiple functions. |
| Hot-reload | Saving a function compiles and deploys it immediately. Same in-flight behavior caveats as pipeline components apply. |
Wrap function logic in try/catch. Unhandled exceptions surface as errors at the call site — map, port, pipeline, expression — and are harder to diagnose than a clear error message from your code.
The [CustomFunction] name is immutable once any caller depends on it. Use descriptive, stable identifiers.
Pass connection strings, API keys, and environment-specific values as parameters rather than hardcoding them. This makes functions reusable across environments.
SaxonHE 12.9 includes a comprehensive function library. Use custom functions only when built-in XSLT/XPath functions cannot meet the requirement.
Use the class name as a logical namespace. Group related functions together (e.g., StringFunctions, DatabaseLookup, DateHelpers).
Same as pipeline components — save changes when no messages are actively being processed to avoid unpredictable in-flight behavior.
Ready to transform
Download the custom function template, write your logic, and reference it from your XSLT maps, port and adapter properties, pipeline components, or routing expressions. Art2link ESB handles compilation and deployment automatically.