2010-02-23 23 views
8

我想在XPathExpression對象中使用Microsoft XPath擴展函數(例如ms:string-compare http://msdn.microsoft.com/en-us/library/ms256114.aspx)。在XPathExpression中使用ms:xpath函數

這些功能是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

我期待一個解決方案是可行的,但經過一段搜索我找到了你的報價。由於這不是我自己的,所以我將其添加爲註釋 [quote] 不幸的是,XPathNavigator不支持MIcrosoft ms:擴展 函數,它們僅在XSLT上下文中可用。 查看http://www.tkachenko.com/blog/archives/000649.html中的 示例代碼,您可以使用這些代碼將這些擴展功能掛接到 XPathNavigator。 Oleg Tkachenko [XML MVP,MCPD] [endquote] 所以,不是我自己的答案,但仍然可以使用我的想法。 – 2010-02-27 00:45:01

+0

非常感謝...我希望以某種方式獲得XsltContext的實例化,但似乎唯一的方法是重寫它並實現所有抽象方法:-( – Filini 2010-03-05 18:19:41

回答

5

這些ms前綴函數不包含在.net框架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

可以使用編譯後的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; 
     } 
    } 
}