XslCompiledTransform implements the following useful MSXML extension functions. But what if you need to use them in XPath-only context - when evaluating XPath queries using XPathNavigator?
Function | Signature and description |
---|---|
ms:string-compare | number ms:string-compare(string x, string y[, string language[, string options]]) Performs lexicographical string comparison. |
ms:utc | string ms:utc(string time) Converts the prefixed date/time related values into Coordinated Universal Time and into a fixed (normalized) representation that can be sorted and compared lexicographically. |
ms:namespace-uri | string ms:namespace-uri(string name) Resolves the prefix part of a qualified name into a namespace URI. |
ms:local-name | string ms:local-name(string name) Returns the local name part of a qualified name by stripping out the namespace prefix. |
ms:number | number ms:number(string value) Takes a string argument in XSD format and converts it into an XPath number. |
ms:format-date | string ms:format-date(string datetime[, string format[, string locale]]) Converts standard XSD date formats to characters suitable for output. |
ms:format-time | string ms:format-time(string datetime[, string format[, string locale]]) Converts standard XSD time formats to characters suitable for output. |
Here is a quick sketch on how to leverage XslCompiledTransform implementation of these functions to create custom XslContext class. The code above implments only ms:string-compare(), but other functions can be added in a similar way. Here is how you use it:
string xml = ""; XPathExpression expr = XPathExpression.Compile("ms:string-compare(value[1], value[2])"); MsXsltContext ctx = new MsXsltContext(); ctx.AddNamespace("ms", "urn:schemas-microsoft-com:xslt"); expr.SetContext(ctx); XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); XPathNavigator nav = doc.DocumentElement.CreateNavigator(); Console.WriteLine(nav.Evaluate(expr)); ABCD EFGH
And here is sample MsXsltContext implementation:
using System; using System.Xml.Xsl; using System.Xml.XPath; using System.Xml; using System.Reflection; using System.Xml.Xsl.Runtime; using System.Globalization; public class MsXsltContext : XsltContext { // Function to resolve references to my custom functions. public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] argTypes) { string namespaceUri = this.LookupNamespace(prefix); if (namespaceUri == "urn:schemas-microsoft-com:xslt") { switch (name) { case "string-compare": return new MsExtensionFunction(name, 2, 4, new XPathResultType[] { XPathResultType.String, XPathResultType.String, XPathResultType.String, XPathResultType.String }, XPathResultType.Number); } } return null; } public override IXsltContextVariable ResolveVariable(string prefix, string name) { return null; } public override int CompareDocument(string baseUri, string nextBaseUri) { return 0; } public override bool PreserveWhitespace(XPathNavigator node) { return true; } public override bool Whitespace { get { return true; } } } public class MsExtensionFunction : IXsltContextFunction { private XPathResultType[] argTypes; private XPathResultType returnType; private string name; private int minArgs; private int maxArgs; private MethodInfo method; public int Minargs { get { return minArgs; } } public int Maxargs { get { return maxArgs; } } public XPathResultType[] ArgTypes { get { return argTypes; } } public XPathResultType ReturnType { get { return returnType; } } public MsExtensionFunction(string name, int minArgs, int maxArgs, XPathResultType[] argTypes, XPathResultType returnType) { this.name = name; this.minArgs = minArgs; this.maxArgs = maxArgs; this.argTypes = argTypes; this.returnType = returnType; } public object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) { switch (name) { case "string-compare": if (method == null) { method = typeof(XsltFunctions).GetMethod("MSStringCompare"); } object[] fullArgs = new object[maxArgs]; fullArgs[0] = ConvertToString(args[0]); fullArgs[1] = ConvertToString(args[1]); fullArgs[2] = args.Length > 2 ? ConvertToString(args[2]) : ""; fullArgs[3] = args.Length > 3 ? ConvertToString(args[3]) : ""; return method.Invoke(null, fullArgs); } return null; } private static string ConvertToString(object argument) { XPathNodeIterator it = argument as XPathNodeIterator; if (it != null) { return IteratorToString(it); } else { return ToXPathString(argument); } } private static string IteratorToString(XPathNodeIterator it) { if (it.MoveNext()) { return it.Current.Value; } return string.Empty; } private static String ToXPathString(Object value) { string s = value as string; if (s != null) { return s; } else if (value is double) { return ((double)value).ToString("R", NumberFormatInfo.InvariantInfo); } else if (value is bool) { return (bool)value ? "true" : "false"; } else { return Convert.ToString(value, NumberFormatInfo.InvariantInfo); } } }Don't forget to add a reference to the System.Data.SqlXml.dll.
Leave a comment