Using ms: xpath functions inside XPathExpression

早过忘川 提交于 2019-11-29 06:54:22

These ms prefixed functions are not included in .net framework dom classes. you need to create your custom functions to do same thing.

you may use sample code below;

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

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

nav.Select(xpath, nsManager );

or

XPathExpression compiledXPath = XPathExpression.Compile(xpath);

compiledXPath.SetContext(nsManager);

nav.Evaluate(compiledXPath);

you will need these classes;

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;
        }
    }
}

You can use the compiled XPath, or dynamic with Linqtoxml and 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);

My compare function:

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;
        }
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!