HtmlAgilityPack XPath 大小写忽略

发布于 2025-01-02 18:31:30 字数 221 浏览 3 评论 0原文

当我使用

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

它时不起作用,但是当我使用原始文档中使用的相同大小写时,它工作得很好:

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

所以问题是如何设置忽略大小写?

When I use

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

it doesn't work, but when I use the same case that used in original document it works good:

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

So the question is how can I set case ignoring?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

娇妻 2025-01-09 18:31:30

如果实际值是未知的情况,我认为你必须使用translate。我相信是这样的:

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

这是 hack,但它是 XPath 1.0 中唯一的选择(除了与大写相反的情况)。

If the actual value is an unknown case, I think you have to use translate. I believe it's:

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

This is the hack, but it's the only option in XPath 1.0 (except the opposite to upper-case).

眼中杀气 2025-01-09 18:31:30

如果您需要更全面的解决方案,您可以为 XPath 处理器编写一个扩展函数,该函数将执行不区分大小写的比较。这是相当多的代码,但你只写一次。

实现扩展后,您可以按如下方式编写查询,

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

其中 Extensions:CaseInsensitiveComparison 是下面示例中实现的扩展函数。

注意:这还没有经过充分测试,我只是将其放在一起以用于此响应,因此错误处理等不存在!

以下是自定义 XSLT 上下文的代码,它提供一个或多个扩展函数

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

然后您可以在 XPath 查询中使用上述扩展函数,这是我们案例的示例

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

If you need a more comprehensive solution, you can write an extension function for the XPath processor which will perform a case insensitive comparison. It is quite a bit of code, but you only write it once.

After implementing the extension you can write your query as follows

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

Where Extensions:CaseInsensitiveComparison is the extension function implemented in the sample below.

NOTE: this is not well tested I just threw it together for this response so the error handling etc. is non-existent!

The following is the code for the custom XSLT Context which provides one or more extension functions

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

You can then use the above extension function in your XPath queries, here is an example for our case

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
  }
}
红焚 2025-01-09 18:31:30

我就是这样做的:

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;

This is how I do it:

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;
豆芽 2025-01-09 18:31:30

或者使用新的 Linq 语法,它应该支持不区分大小写的匹配:

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

但是您必须对属性进行丑陋的 null 检查,以防止出现 NullReferenceException...

Alternatively use the new Linq syntax which should support case insensitive matching:

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

But you have to do an ugly null check for the attributes in order to prevent a NullReferenceException...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文