为什么XPath CONTAINS(text(),'substring')不能按预期工作?

2022-02-21 00:00:00 xml xpath html

假设我有一段HTML,如下所示:

<a>Ask Question<other/>more text</a>

我可以匹配这段XPath:

//a[text() = 'Ask Question']

或.

//a[text() = 'more text']

或者我可以使用点来匹配整个内容:

//a[. = 'Ask Questionmore text']

This post描述了.(点)和text()之间的区别,但简而言之,第一个返回单个元素,而后者返回一个元素列表。但这就是我觉得有点奇怪的地方。因为text()可以用来匹配列表中的任何一个元素,但是对于XPath函数contains()就不是这样了。如果我这样做:

//a[contains(text(), 'Ask Question')]

.我收到以下错误:

错误:CONTAINS()的第一个参数要求的基数为1或0

使用完全匹配(等于)时text()如何起作用,但在部分匹配(包含)时不起作用?


解决方案

对于此标记,

<a>Ask Question<other/>more text</a>

请注意,a元素有一个文本节点子元素("Ask Question")、一个空元素子元素(other)和第二个文本节点子元素("more text")。

下面是如何根据标记对//a[contains(text(),'Ask Question')]求值时所发生情况进行推理:

  1. contains(x,y)预期x为字符串,但text()匹配两个文本节点。
  2. 在XPath 1.0中,将多个节点转换为字符串的规则为this:
通过返回的string-value将节点集转换为字符串 节点集中在document order中的第一个节点。如果 节点集为空,则返回空字符串。[已添加强调]

  1. 在XPath 2.0+中,向需要字符串的函数提供文本节点序列是错误的,因此contains(text(),'substr')将导致多个匹配的文本节点出错。

在您的情况下.

  • XPath 1.0会将contains(text(),'Ask Question')视为

    contains('Ask Question','Ask Question')
    

    ,即true。另一方面,请务必注意,在XPath 1.0中,contains(text(),'more text')的计算结果将为false。如果不知道上面的(1)-(3),这可能是违反直觉的。

  • XPath 2.0会将其视为错误。

更好的替代方案

  • 如果目标是查找字符串值包含子字符串的所有a元素,
    "Ask Question"

    //a[contains(.,'Ask Question')]
    

    这是最常见的要求。

  • 如果目标是查找直接文本节点子级等于"Ask Question"的所有a元素:

    //a[text()='Ask Question']
    

    当希望从a中的后代元素中排除字符串时(例如,如果您想要a

    ),这会很有用
    <a>Ask Question<other/>more text</a>
    

    但不是这个a

    <a>more text before <not>Ask Question</not> more text after</a>
    

另请参阅

  • How contains() handles a nodeset first arg
  • How to use XPath contains() for specific text?
  • Testing text() nodes vs string values in XPath

相关文章