2010-02-23 14 views
8

Próbuję użyć funkcji rozszerzenia Microsoft XPath (takich jak ms: string-compare http://msdn.microsoft.com/en-us/library/ms256114.aspx) wewnątrz obiektu XPathExpression.Używanie funkcji ms: xpath wewnątrz XPathExpression

Te funkcje są rozszerzeniami wewnątrz biblioteki MSXML, i jeśli mogę użyć ich w XslCompiledTransform (po prostu dodanie nazw „MS”) działają jak urok:

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

Potem spróbował wykorzystać je w sposób 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); 

Ale otrzymuję wyjątek „XsltContext jest potrzebna dla tego zapytania z powodu nieznanej funkcji”.

XsltContext to specyficzny obiekt XmlNamespaceManager, ale nie wiem, czy można go utworzyć bez rzeczywistej XslCompiledTransform (to streszczenie) i użyć go jako kontekstu wyrażeń.

Czy jest jakiś sposób to zrobić (lub jakikolwiek inny sposób użycia ms: rozszerzenia wewnątrz XPathExpression)?

+2

Spodziewałem rozwiązanie byłoby możliwe, ale po jakimś przeszukanie znalazłem dla ciebie wycenę. Ponieważ nie jest to moje własne, dodam je jako komentarz [quote] Niestety XPathNavigator nie obsługuje funkcji MIcrosoft ms: extention , są one dostępne tylko w kontekście XSLT. Spójrz na http://www.tkachenko.com/blog/archives/000649.html dla przykładowego kodu , za pomocą którego możesz podłączyć te funkcje rozszerzeń do XPathNavigator . Oleg Tkachenko [XML MVP, MCPD] [endquote] Więc, nie moja własna odpowiedź, ale nadal coś, co można użyć myślę. –

+0

dziękuję bardzo ... Miałem nadzieję, że jakoś powstanie instancja XsltContext, ale wydaje się, że jedynym sposobem jest obejście i wdrożenie wszystkich abstrakcyjnych metod :-( – Filini

Odpowiedz

5

Te wstępnie ustawione funkcje ms nie są uwzględnione w klasach DOM .net. musisz utworzyć własne funkcje, aby zrobić to samo.

możesz użyć przykładowego kodu poniżej;

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

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

nav.Select(xpath, nsManager); 

lub

XPathExpression compiledXPath = XPathExpression.Compile(xpath); 

compiledXPath.SetContext(nsManager); 

nav.Evaluate(compiledXPath); 

trzeba będzie tych klas;

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

Można użyć skompilowany XPath lub dynamiczny z Linqtoxml i 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); 

Moje porównanie funkcji:

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