c#
  • xpath
  • .net-2.0
  • html-agility-pack
  • case-sensitive
  • 2012-02-05 13 views 6 likes 
    6

    Cuando usocaso HtmlAgilityPack XPath ignorando

    SelectSingleNode("//meta[@name='keywords']") 
    

    que no funciona, pero cuando se utiliza el mismo caso que se utiliza en el documento original, funciona bien:

    SelectSingleNode("//meta[@name='Keywords']") 
    

    Así que la pregunta es ¿Cómo puedo configurar el caso ignorando?

    +0

    XPath es deliberadamente las mayúsculas y minúsculas? – CarneyCode

    +0

    @Carnotaurus Sí. – Tomalak

    Respuesta

    4

    Si necesita una solución más completa, puede escribir una función de extensión para el procesador XPath que realizará una comparación insensible a mayúsculas y minúsculas. Es bastante código, pero solo lo escribes una vez.

    Después de implementar la extensión que puede escribir su consulta como sigue

    "//meta[@name[Extensions:CaseInsensitiveComparison('Keywords')]]" 
    

    Dónde Extensions:CaseInsensitiveComparison es la función de extensión implementado en el ejemplo siguiente.

    NOTA: esto no se ha probado bien ¡Lo tiré para esta respuesta, por lo que el manejo de errores, etc., no existe!

    El siguiente es el código para el Contexto XSLT personalizada que proporciona uno o más de extensión funciones

    using System; 
    using System.Xml.XPath; 
    using System.Xml.Xsl; 
    using System.Xml; 
    using HtmlAgilityPack; 
    
    public class XsltCustomContext : XsltContext 
    { 
        public const string NamespaceUri = "http://XsltCustomContext"; 
    
        public XsltCustomContext() 
        { 
        } 
    
        public XsltCustomContext(NameTable nt) 
        : base(nt) 
        {  
        } 
    
        public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] ArgTypes) 
        { 
        // Check that the function prefix is for the correct namespace 
        if (this.LookupNamespace(prefix) == NamespaceUri) 
        { 
         // Lookup the function and return the appropriate IXsltContextFunction implementation 
         switch (name) 
         { 
         case "CaseInsensitiveComparison": 
          return CaseInsensitiveComparison.Instance; 
         } 
        } 
    
        return null; 
        } 
    
        public override IXsltContextVariable ResolveVariable(string prefix, string name) 
        { 
        return null; 
        } 
    
        public override int CompareDocument(string baseUri, string nextbaseUri) 
        { 
        return 0; 
        } 
    
        public override bool PreserveWhitespace(XPathNavigator node) 
        { 
        return false; 
        } 
    
        public override bool Whitespace 
        { 
        get { return true; } 
        } 
    
        // Class implementing the XSLT Function for Case Insensitive Comparison 
        class CaseInsensitiveComparison : IXsltContextFunction 
        { 
        private static XPathResultType[] _argTypes = new XPathResultType[] { XPathResultType.String }; 
        private static CaseInsensitiveComparison _instance = new CaseInsensitiveComparison(); 
    
        public static CaseInsensitiveComparison Instance 
        { 
         get { return _instance; } 
        }  
    
        #region IXsltContextFunction Members 
    
        public XPathResultType[] ArgTypes 
        { 
         get { return _argTypes; } 
        } 
    
        public int Maxargs 
        { 
         get { return 1; } 
        } 
    
        public int Minargs 
        { 
         get { return 1; } 
        } 
    
        public XPathResultType ReturnType 
        { 
         get { return XPathResultType.Boolean; } 
        } 
    
        public object Invoke(XsltContext xsltContext, object[] args, XPathNavigator navigator) 
        {     
         // Perform the function of comparing the current element to the string argument 
         // NOTE: You should add some error checking here. 
         string text = args[0] as string; 
         return string.Equals(navigator.Value, text, StringComparison.InvariantCultureIgnoreCase);   
        } 
        #endregion 
        } 
    } 
    

    A continuación, puede utilizar la función de extensión más arriba en sus consultas XPath, aquí es un ejemplo para nuestro caso

    class Program 
    { 
        static string html = "<html><meta name=\"keywords\" content=\"HTML, CSS, XML\" /></html>"; 
    
        static void Main(string[] args) 
        { 
        HtmlDocument doc = new HtmlDocument(); 
        doc.LoadHtml(html); 
    
        XPathNavigator nav = doc.CreateNavigator(); 
    
        // Create the custom context and add the namespace to the context 
        XsltCustomContext ctx = new XsltCustomContext(new NameTable()); 
        ctx.AddNamespace("Extensions", XsltCustomContext.NamespaceUri); 
    
        // Build the XPath query using the new function 
        XPathExpression xpath = 
         XPathExpression.Compile("//meta[@name[Extensions:CaseInsensitiveComparison('Keywords')]]"); 
    
        // Set the context for the XPath expression to the custom context containing the 
        // extensions 
        xpath.SetContext(ctx); 
    
        var element = nav.SelectSingleNode(xpath); 
    
        // Now we have the element 
        } 
    } 
    
    +0

    ¿se puede aplicar esto al nombre del nodo? –

    8

    Si el valor real es un caso desconocido, creo que debe usar translate. Creo que es:

    SelectSingleNode("//meta[translate(@name,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='keywords']") 
    

    Este es el truco, pero es la única opción en XPath 1.0 (excepto lo opuesto a mayúsculas).

    2

    Así es como lo hago:

    HtmlNodeCollection MetaDescription = document.DocumentNode.SelectNodes("//meta[@name='description' or @name='Description' or @name='DESCRIPTION']"); 
    
    string metaDescription = MetaDescription != null ? HttpUtility.HtmlDecode(MetaDescription.FirstOrDefault().Attributes["content"].Value) : string.Empty; 
    
    +0

    Su enfoque no es tan universal como el de Chris Taylor. La respuesta de Chris llama la atención sobre cualquier combinación de casos de char. – kseen

    +1

    @kseen Lo sé, pero realmente, ¿es posible que alguien ponga algo como "KeYwOrDs"? Estas son tres formas comunes, y si alguien escribe un nombre así, dudo que pueda analizar algo de ese documento HTML. Esta es una solución fuera de caja que requiere dos líneas de código y funciona bien para la mayoría de los casos, pero todo depende de su requerimiento. – formatc

    +0

    Estoy tratando de mantener la regla "nunca confíes en la opinión del usuario" y yo también te aconsejo. – kseen

    1

    Como alternativa, utilice º e nueva sintaxis LINQ que debe apoyar caso de coincidencia insensible:

     node = doc.DocumentNode.Descendants("meta") 
          .Where(meta => meta.Attributes["name"] != null) 
          .Where(meta => string.Equals(meta.Attributes["name"].Value, "keywords", StringComparison.OrdinalIgnoreCase)) 
          .Single(); 
    

    Pero hay que hacer una comprobación nula fea para los atributos con el fin de prevenir una NullReferenceException ...

    Cuestiones relacionadas