在XPathExpression中使用ms:xpath函数

时间:2010-02-23 17:58:12

标签: extension-methods xpath

我正在尝试在XPathExpression对象中使用Microsoft XPath扩展函数(例如ms:string-compare http://msdn.microsoft.com/en-us/library/ms256114.aspx)。

这些函数是MSXML库中的扩展,如果我在XslCompiledTransform中使用它们(只是添加“ms”命名空间),它们就像魅力一样:

var xsl =
    @"
<?xml version=""1.0"" encoding=""UTF-8""?>
<xsl:stylesheet version=""2.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"" 
        xmlns:xs=""http://www.w3.org/2001/XMLSchema"" 
        xmlns:fn=""http://www.w3.org/2005/xpath-functions"" 
        xmlns:ms=""urn:schemas-microsoft-com:xslt"">
 <xsl:output method=""xml"" version=""1.0"" encoding=""UTF-8"" indent=""yes""/>
 <xsl:template match=""/Data"">
  <xsl:element name=""Result"">
   <xsl:value-of select=""ms:string-compare(@timeout1, @timeout2)""/>
  </xsl:element>
 </xsl:template>
</xsl:stylesheet>";

var xslDocument = new XmlDocument();
xslDocument.LoadXml(xsl);

var transform = new XslCompiledTransform();
transform.Load(xslDocument);

然后我尝试在XPathExpression中使用它们:

XPathNavigator nav = document.DocumentElement.CreateNavigator();
XPathExpression expr = nav.Compile("ms:string-compare(/Data/@timeout1, /Data/@timeout2)");

XmlNamespaceManager manager = new XmlNamespaceManager(document.NameTable);
manager.AddNamespace("ms", "urn:schemas-microsoft-com:xslt");
expr.SetContext(manager);

nav.Evaluate(expr);

但我得到一个例外“由于函数未知,此查询需要XsltContext”。

XsltContext是一个特定的XmlNamespaceManager,但我不知道是否可以在没有实际的XslCompiledTransform(它是抽象的)的情况下实例化它并将其用作我的表达式上下文。

有没有办法做到这一点(或者在XPathExpression中使用ms:扩展的任何其他方式)?

2 个答案:

答案 0 :(得分:5)

这些ms前缀函数不包含在.net framework dom类中。你需要创建自定义函数来做同样的事情。

您可以使用以下示例代码;

string xpath = "my:string-compare('1','1)";

System.Xml.XmlNamespaceManager nsManager = new XsltContext();

nav.Select(xpath, nsManager );

XPathExpression compiledXPath = XPathExpression.Compile(xpath);

compiledXPath.SetContext(nsManager);

nav.Evaluate(compiledXPath);

你需要这些课程;

public class XsltContext : System.Xml.Xsl.XsltContext
{
    public XsltContext()
    {
        Initialize();
    }

    public XsltContext(System.Xml.NameTable nameTable)
        : base(nameTable)
    {
        Initialize();
    }

    private void Initialize()
    {
        RegisterFunction("my", "string-compare", typeof(StringCompare));
    }

    public override string LookupNamespace(string prefix)
    {
        return base.LookupNamespace(prefix);
    }

    public override int CompareDocument(string baseUri, string nextbaseUri)
    {
        return string.CompareOrdinal(baseUri, nextbaseUri);
    }

    public override bool PreserveWhitespace(System.Xml.XPath.XPathNavigator node)
    {
        return false;
    }

    public void RegisterFunction(string prefix, string name, Type function)
    {
        if (function == null)
            throw new ArgumentNullException("function");

        if (name == null)
            throw new ArgumentNullException("name");

        functions[prefix + ":" + name] = function;
    }

    Dictionary<string, Type> functions = new Dictionary<string, Type>();

    public override System.Xml.Xsl.IXsltContextFunction ResolveFunction(string prefix, string name, System.Xml.XPath.XPathResultType[] argTypes)
    {
        Type functionType = null;

        if (functions.TryGetValue(prefix + ":" + name, out functionType))
        {
            System.Xml.Xsl.IXsltContextFunction function = Activator.CreateInstance(functionType) as System.Xml.Xsl.IXsltContextFunction;

            return function;
        }

        return null;
    }

    public override System.Xml.Xsl.IXsltContextVariable ResolveVariable(string prefix, string name)
    {
        return null;
    }

    public override bool Whitespace
    {
        get
        {
            return false;
        }
    }

    internal static string GetValue(object v)
    {
        if (v == null)
            return null;

        if (v is System.Xml.XPath.XPathNodeIterator)
        {
            foreach (System.Xml.XPath.XPathNavigator n in v as System.Xml.XPath.XPathNodeIterator)
                return n.Value;
        }

        return Convert.ToString(v);
    }

}

class StringCompare : System.Xml.Xsl.IXsltContextFunction
{
    public System.Xml.XPath.XPathResultType[] ArgTypes
    {
        get
        {
            return new System.Xml.XPath.XPathResultType[]
            {
                System.Xml.XPath.XPathResultType.String,
                System.Xml.XPath.XPathResultType.String,
                System.Xml.XPath.XPathResultType.String
            };
        }
    }

    public object Invoke(System.Xml.Xsl.XsltContext xsltContext, object[] args, System.Xml.XPath.XPathNavigator docContext)
    {
        string arg1 = XsltContext.GetValue(args[0]);
        string arg2 = XsltContext.GetValue(args[1]);

        string locale = "en-US";

        if (args.Length > 2)
            locale = XsltContext.GetValue(args[2]);

        System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.GetCultureInfo(locale);

        return string.Compare(arg1, arg2, false, culture);
    }

    public int Maxargs
    {
        get
        {
            return 3;
        }
    }

    public int Minargs
    {
        get 
        {
            return 2;
        }
    }

    public System.Xml.XPath.XPathResultType ReturnType
    {
        get
        {
            return System.Xml.XPath.XPathResultType.Number;
        }
    }
}

答案 1 :(得分:3)

您可以使用已编译的XPath,或使用Linqtoxml和XElement动态:

        XPathCustomContext context = new XPathCustomContext(new NameTable());
        context.AddNamespace("windward", XPathCustomContext.Namespace);

        XmlDocument document = new XmlDocument();
        string records = @"
        <records>
            <record id=""m""/>
            <record id=""M""/>
            <record id=""l""/>
        </records>
        ";
        document.LoadXml(records);

        string xpath = @"//record[my:string-compare(@id,""m"")]";

        //solution 1
        XPathExpression compiledXPath = XPathExpression.Compile(xpath, context);
        compiledXPath.SetContext(context);
        XPathNavigator nav = document.CreateNavigator();
        object res = nav.Evaluate(compiledXPath);

        //solution 2
        XElement elm = XElement.Parse(records);
        IEnumerable<XElement> targets = elm.XPathSelectElements(xpath, context);

我的比较功能:

public class MyStringCompare : IWindwardContextFunction
{
    public System.Xml.XPath.XPathResultType[] ArgTypes
    {
        get
        {
            return new System.Xml.XPath.XPathResultType[]
        {
            System.Xml.XPath.XPathResultType.String,
            System.Xml.XPath.XPathResultType.String,
            System.Xml.XPath.XPathResultType.String
        };
        }
    }
    /// <summary>
    /// The function name.
    /// </summary>
    public string FunctionName
    {
        get { return "string-compare"; }
    }

    public object Invoke(System.Xml.Xsl.XsltContext xsltContext, object[] args, System.Xml.XPath.XPathNavigator docContext)
    {

        string arg1 = "";// Convert.ToString(args[0]);
        object arg1Obj = args[0];
        IEnumerable list = arg1Obj as IEnumerable;
        if (arg1Obj != null)
        {
            IEnumerator listit = list.GetEnumerator();
            listit.MoveNext();

            XPathNavigator nav = (XPathNavigator)listit.Current;
            arg1 = nav.Value;
        }

        string arg2 = Convert.ToString(args[1]);

        string locale = "en-US";

        if (args.Length > 2)
            locale = Convert.ToString(args[2]);

        System.Globalization.CultureInfo culture = CultureInfo.GetCultureInfo(locale);

        return string.Compare(arg1, arg2, true) == 0;
    }

    public int Maxargs
    {
        get
        {
            return 3;
        }
    }

    public int Minargs
    {
        get
        {
            return 2;
        }
    }

    public System.Xml.XPath.XPathResultType ReturnType
    {
        get
        {
            return System.Xml.XPath.XPathResultType.Number;
        }
    }
}