HtmlAgilityPack XPath caso ignorando

.net-2.0 c# case-sensitive html-agility-pack xpath

Pregunta

Cuando yo uso

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

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

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

Entonces la pregunta es ¿cómo puedo configurar el caso de ignorar?

Respuesta aceptada

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 que no distinga mayúsculas y minúsculas. Es bastante código, pero solo lo escribes una vez.

Después de implementar la extensión puede escribir su consulta de la siguiente manera

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

Donde Extensions:CaseInsensitiveComparison es la función de extensión implementada en el siguiente ejemplo.

NOTA: esto no está bien probado, simplemente lo junté para esta respuesta, por lo que el manejo de errores, etc. ¡no existe!

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

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

Luego puede usar la función de extensión anterior en sus consultas XPath, aquí hay 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
  }
}

Respuesta popular

Si el valor real es un caso desconocido, creo que tiene que usar traducir. 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 contrario a mayúsculas).



Related

Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow