2010-02-23 21 views
8

Estoy tratando de usar las funciones de extensión XPath de Microsoft (como ms: string-compare http://msdn.microsoft.com/en-us/library/ms256114.aspx) dentro de un objeto XPathExpression.Uso de las funciones ms: xpath dentro de XPathExpression

Estas funciones son extensiones dentro de la biblioteca MSXML, y si las uso en un XslCompiledTransform (simplemente añadiendo el espacio de nombres "MS") que trabajan como un encanto:

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

Entonces les intentado utilizar en una 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); 

Pero consigo una excepción "XsltContext se necesita para esta consulta debido a una función desconocida".

XsltContext es un XmlNamespaceManager específico, pero no sé si es posible crear una instancia sin un XslCompiledTransform real (es abstracto) y usarlo como mi contexto de expresión.

¿Hay alguna manera de hacer esto (o de alguna otra forma de usar ms: extensiones dentro de XPathExpression)?

+2

que estaba esperando una solución que sería posible, pero después de algún buscando encontré un presupuesto para ti. Como esto no es mío, lo agrego como comentario [quote] Desafortunadamente XPathNavigator no es compatible con MIcrosoft ms: funciones de extensión , solo están disponibles en contexto XSLT. Eche un vistazo a http://www.tkachenko.com/blog/archives/000649.html para obtener un código de ejemplo que puede usar para enganchar esas funciones de extensión en XPathNavigator. Oleg Tkachenko [XML MVP, MCPD] [endquote] Así que, no es mi propia respuesta, pero todavía hay algo que podría usar, creo. –

+0

muchas gracias ... Esperaba obtener un XsltContext instanciado de alguna manera, pero parece que la única manera es anularlo e implementar todos los métodos abstractos :-( – Filini

Respuesta

5

Estas funciones prefijadas ms no se incluyen en las clases dom framework .net. necesita crear sus funciones personalizadas para hacer lo mismo.

puede usar el código de muestra a continuación;

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

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

nav.Select(xpath, nsManager); 

o

XPathExpression compiledXPath = XPathExpression.Compile(xpath); 

compiledXPath.SetContext(nsManager); 

nav.Evaluate(compiledXPath); 

necesitará estas clases;

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

Puede utilizar el XPath compilada, o dinámico con Linqtoxml y 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); 

Mi función de comparación:

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