Использование функций ms: xpath внутри XPathExpression - PullRequest
8 голосов
/ 23 февраля 2010

Я пытаюсь использовать функции расширения Microsoft XPath (например, ms: string-сравнение http://msdn.microsoft.com/en-us/library/ms256114.aspx) внутри объекта XPathExpression.

Эти функции являются расширениями внутри библиотеки 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 (он абстрактный) и использовать его как контекст моего выражения.

Есть ли способ сделать это (или любой другой способ использовать ms: extensions внутри XPathExpression)?

Ответы [ 2 ]

5 голосов
/ 07 января 2011

Эти префиксные функции 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;
        }
    }
}
3 голосов
/ 25 мая 2013

Вы можете использовать скомпилированный 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;
        }
    }
}
...