Using Microsoft's Extended XPath functions in BizTalk#
It's not that well documented, but Microsoft have a bunch of extra functions you can use from XPath expressions within XSLT.
You can find the documentation on them here.

What isn't made apparent is that none of them work with XslTransform: a subset of them will work with XslCompiledTransform, and the others only work with MSXML 4.0 onwards.

This becomes obvious if you try and use these functions, or if you read this blog post from the Microsoft XML team on the XslCompiledTransform class.

Being the curious cat I am, I wondered if there was any way to use the functions from within XslTransform - if there was, I figured you could use the functions in a BizTalk Map.

Short answer: No, but you can use them via Extension Objects (I'll show you how later in this post).
Long Answer:
If you look at the source code for the System.Xml.dll assembly (either via .NET Reflector, or the SSCLI, or (even better) downloading the official source code using NetMassDownloader) you can look at how XSLT containing these functions is handled.

[Note: Unfortunately, a lot of the "good stuff" is actually implemented in the System.Data.SqlXml.dll assembly: this assembly contains the System.Xml.Xsl.Runtime namespace in which most of the logic is defined.
The assembly source is not included in the source you can download using NetMassDownloader, so the only way of viewing it is via .NET reflector, or the SSCLI. Interestingly, the System.Data.SqlXml.dll assembly no longer contains any SqlXml related code - it was all migrated to System.Data.dll and Microsoft.Data.SqlXml.dll with the release of .NET 2.0 from what I can see - all that's left is internal XSLT code, so the assembly would now be better called System.Xml.Xslt.dll]

As an example, we'll look at ms:format-date: this will format an XSD-style date/time using a given format string - pretty much the same as using DateTime.ToString(string) in .NET.
If you do a search for format-date in the source code, you find all of the implementation in the System.Data.SqlXml.dll assembly.

Firstly, you find a method called ResolveFunction in the QilGenerator class (in the System.Xml.Xsl.Xslt namespace):
(Note: Qil refers to the process of compiling XSLT to MSIL and thence into a managed assembly via an Abstract Syntax Tree (AST) - it's an interesting topic and worthy of a post in it's own right! Give me time...)
// NOTE: DO NOT call QilNode.Clone() while executing this method since fixup nodes cannot be cloned
QilNode IXPathEnvironment.ResolveFunction(string prefix, string name, IList<QilNode> args, IFocus env) {
else if (name == "format-date" || name == "format-time")
    FunctionInfo.CheckArity(/*minArg:*/1, /*maxArg:*/3, name, args.Count);
    bool fwdCompat = (xslVersion == XslVersion.ForwardsCompatible);
    return f.InvokeMsFormatDateTime
        /*format: */1 < args.Count ? f.ConvertToString(args[1]) : f.String(string.Empty),
        /*lang:    */2 < args.Count ? f.ConvertToString(args[2]) : f.String(string.Empty),
        /*isDate: */f.Boolean(name == "format-date")
What the ResolveFunction method does is attempt to map a call to an external function (defined in the urn:schemas-microsoft-com:xslt namespace) to a method defined in the XsltFunctions class.

In this case, the method InvokeMsFormatDateTime in the XsltQilFactory class (again in the System.Xml.Xsl.Xslt namespace) is called to create an early-bound method link (note that XsltMethods.MSFormatDateTime returns a System.Refelection.MethodInfo object which can be used to call the method via reflection):
public QilNode InvokeMsFormatDateTime(QilNode datetime, QilNode format, QilNode lang, QilNode isDate) {
    return XsltInvokeEarlyBound(QName("ms:format-date-time"),
        XsltMethods.MSFormatDateTime, T.StringX, new QilNode[] { datetime, format, lang, isDate }

The XsltMethods class contains this entry for MSFormatDateTime:
internal static class XsltMethods
    public static readonly MethodInfo MSFormatDateTime = GetMethod(typeof(XsltFunctions), "MSFormatDateTime");

Which in turn points to the MSFormatDateTime method in the XsltFunctions class (defined in the System.Xml.Xsl.Runtime namespace):
namespace System.Xml.Xsl.Runtime {
    using Res = System.Xml.Utils.Res;
public static class XsltFunctions {
private static readonly CompareInfo compareInfo = CultureInfo.InvariantCulture.CompareInfo;
// string ms:format-date(string datetime[, string format[, string language]])
// string ms:format-time(string datetime[, string format[, string language]])
// Format xsd:dateTime as a date/time string for a given language using a given format string.
// * Datetime contains a lexical representation of xsd:dateTime. If datetime is not valid, the
// empty string is returned.
// * Format specifies a format string in the same way as for GetDateFormat/GetTimeFormat system
// functions. If format is the empty string or not passed, the default date/time format for the
// given culture is used.
// * Language specifies a culture used for formatting. If language is the empty string or not
// passed, the current culture is used. If language is not recognized, a runtime error happens.
public static string MSFormatDateTime(string dateTime, string format, string lang, bool isDate)
    try {
         int locale = GetCultureInfo(lang).LCID;

         XsdDateTime xdt;
         if (!XsdDateTime.TryParse(dateTime, XsdDateTimeFlags.AllXsd | XsdDateTimeFlags.XdrDateTime | XsdDateTimeFlags.XdrTimeNoTz, out xdt)) {
            return string.Empty;
         SystemTime st = new SystemTime(xdt.ToZulu());

         StringBuilder sb = new StringBuilder(format.Length + 16);

(interestingly, there's all sorts of good stuff in the XsltFunctions class, including the implementation of the XSLT translate function, and other functions performed by managed code when the XSLT is compiled to MSIL).

Phew! So it turns out that all the good stuff is defined in the XsltFunctions class.
More importantly, the call to ResolveFunction only happens in XslCompiledTransform.
So you can't use any of these functions from XslTransform.
Which means there is no direct way to use them from the BizTalk Mapper.

Calling extended XPath functions via Extension Objects
To call the extended functions from a BizTalk Map, in theory all you need to do is reference them from a Script functoid (using a Script Type of "External Assembly"). Unfortunately if you try this, you'll see that the XsltFunctions class does not appear:

Those of you who were paying attention to the definition of the XsltFunctions class may already understand why: you can't use Static Classes with Extension Objects in a BizTalk Map.
The reason for this is that the extension objects are loaded by BizTalk using this code:

object extension = Assembly.Load(assemblyName).CreateInstance(typeName);

CreateInstance will throw a MissingMethodException if the type passed in does not have a default constructor - which our static class does not.

The BizTalk Mapper knows this, and so does not display static classes in the list of classes in the script functoid.

As far as I am aware (and please correct me if you know of another way) the only way to get around this would be to create a non-static wrapper class round the methods you wanted.
For example if you did this:
namespace System.Data.SqlXmlHelper
    public class Helper
        public static string MSFormatDateTime(string dateTime, string format, string lang, bool isDate)
            return XsltFunctions.MSFormatDateTime(dateTime, format, lang, isDate);

Then you could use the above in a script functoid like this:

The other issue is that some of the functions aren't defined in the System.Data.SqlXml.dll assembly - they're in the unmanaged MSXML dll (from v4.0 onwards) and are designed to be used only when you're using MSXML to process the XSLT.

In order to save you the trouble of creating a wrapper assembly for calling these functions, I've done it for you.
You'll find it (and the source code) in my next post.
Thursday, March 13, 2008 11:21:00 AM (GMT Standard Time, UTC+00:00) #    Comments [0]  |  Trackback Tracked by:
"" (http://mornings... [Pingback]
"" (http://morningside... [Pingback]
"" (http://morningside... [Pingback]
"" ( [Pingback]
"" (http://morningsid... [Pingback]
"" (http://morningside.e... [Pingback]
"" (http://mornings... [Pingback]
"" (http://morningside... [Pingback]
"" ( [Pingback]
"" ( [Pingback]
"" (http://morningsid... [Pingback]
"" ( [Pingback]
"" ( [Pingback]
"" ( [Pingback]
"" ( [Pingback]
"" (http://morningside... [Pingback]
"" (http://morningside.... [Pingback]
"" ( [Pingback]
"" (http://morning... [Pingback]
"" ( [Pingback]
"" (http://morningsi... [Pingback]
"" ( [Pingback]
"" (http://morningsi... [Pingback]


Comments are closed.
All content © 2020, Daniel Probert
On this page
This site
<July 2020>
Blogroll OPML

Powered by: newtelligence dasBlog 2.3.12105.0

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Send mail to the author(s) E-mail

Theme design by Jelle Druyts

Pick a theme: