2010-02-23 12 views
8

Ich versuche, Microsoft XPath-Erweiterungsfunktionen (z. B. ms: string-compare http://msdn.microsoft.com/en-us/library/ms256114.aspx) in einem XPathExpression-Objekt zu verwenden.Verwendung von ms: XPath-Funktionen in XPathExpression

Diese Funktionen sind Erweiterungen innerhalb der MSXML-Bibliothek, und wenn ich sie in einem XslCompiledTransform verwenden (einfaches Hinzufügen der „ms“ Namespace) sie arbeiten wie ein Zauber:

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

Dann habe ich versucht, sie in einem mit 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); 

Aber ich erhalte eine Ausnahme „XsltContext ist für diese Abfrage benötigt aufgrund einer unbekannten Funktion“.

XsltContext ist ein spezifischer XmlNamespaceManager, aber ich weiß nicht, ob es möglich ist, es ohne eine tatsächliche XslCompiledTransform (es ist abstrakt) zu instanziieren und es als meinen Ausdruck Kontext zu verwenden.

Gibt es eine Möglichkeit, dies zu tun (oder eine andere Möglichkeit, ms: Erweiterungen in einem XPathExpression zu verwenden)?

+2

ich eine Lösung erwarten wäre möglich, aber nach einigen Suche ich fand ein Angebot für Sie. Da dies nicht mein eigenes ist, füge ich es als Kommentar hinzu [quote] Leider XPathNavigator nicht unterstützt MIcrosoft ms: Erweiterung Funktionen, sie sind nur im XSLT-Kontext verfügbar. Werfen Sie einen Blick auf http://www.tkachenko.com/blog/archives/000649.html für einen Beispielcode, den Sie verwenden können, um diese Erweiterungsfunktionen in XPathNavigator einhängen. Oleg Tkachenko [XML MVP, MCPD] [endquote] Also, nicht meine eigene Antwort, aber immer noch etwas, was Sie verwenden könnte, denke ich. –

+0

vielen Dank ... Ich hatte gehofft, irgendwie einen XsltContext instanziiert zu bekommen, aber es scheint der einzige Weg zu sein, es zu überschreiben und alle abstrakten Methoden zu implementieren :-( – Filini

Antwort

5

Diese MS-Präfix-Funktionen sind nicht in .NET Framework-Dom-Klassen enthalten. Sie müssen Ihre benutzerdefinierten Funktionen erstellen, um dasselbe zu tun.

können Sie Beispielcode unten verwenden;

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

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

nav.Select(xpath, nsManager); 

oder

XPathExpression compiledXPath = XPathExpression.Compile(xpath); 

compiledXPath.SetContext(nsManager); 

nav.Evaluate(compiledXPath); 

Sie diese Klassen benötigen;

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

Sie können die kompilierten XPath verwenden, oder dynamisch mit Linqtoxml und 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); 

Meine Vergleichsfunktion:

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