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