与具有多个文本子节点的节点一起使用时,XPath contains(text(),'some string') 不起作用

Posted

技术标签:

【中文标题】与具有多个文本子节点的节点一起使用时,XPath contains(text(),\'some string\') 不起作用【英文标题】:XPath contains(text(),'some string') doesn't work when used with node with more than one Text subnode与具有多个文本子节点的节点一起使用时,XPath contains(text(),'some string') 不起作用 【发布时间】:2011-04-09 00:08:28 【问题描述】:

Xpath contains dom4j 有一个小问题...

假设我的 XML 是

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>

假设我想在给定根元素的文本中找到所有具有 ABC 的节点...

所以我需要编写的 xpath 将是

//*[contains(text(),'ABC')]

然而这不是 Dom4j 返回的......这是一个 dom4j 问题还是我理解 xpath 的工作原理。因为该查询只返回 Street 元素而不是 Comment 元素。

DOM 使 Comment 元素成为具有四个标签两个的复合元素

[Text = 'XYZ'][BR][BR][Text = 'ABC'] 

我会假设查询应该仍然返回元素,因为它应该找到元素并在其上运行包含但它没有......

以下查询返回元素,但它返回的远不止元素,它还返回父元素......这对问题来说是不可取的......

//*[contains(text(),'ABC')]

有谁知道只返回元素&lt;Street/&gt;&lt;Comment/&gt; 的xpath 查询?

【问题讨论】:

据我所知,//*[contains(text(),'ABC')] 仅返回 &lt;Street&gt; 元素。它不会返回 &lt;Street&gt;&lt;Comment&gt; 的任何祖先。 【参考方案1】:

&lt;Comment&gt; 标记包含两个文本节点和两个 &lt;br&gt; 子节点。

你的 xpath 表达式是

//*[contains(text(),'ABC')]

为了解决这个问题,

    * 是一个匹配任何元素(即标签)的选择器——它返回一个节点集。 [] 是在该节点集中的每个单独节点上运行的条件。如果它所操作的任何单个节点与括号内的条件匹配,则它匹配。 text() 是一个选择器,它匹配作为上下文节点的所有子节点的所有文本节点——它返回一个节点集。 contains 是一个对字符串进行操作的函数。如果传递一个节点集,则该节点集为converted into a string by returning the string-value of the node in the node-set that is first in document order。因此,它只能匹配&lt;Comment&gt; 元素中的第一个文本节点——即BLAH BLAH BLAH。由于不匹配,您的结果中不会出现&lt;Comment&gt;

你需要把它改成

//*[text()[contains(.,'ABC')]]
    * 是一个匹配任何元素(即标签)的选择器——它返回一个节点集。 外部的[] 是一个条件,作用于该节点集中的每个单独节点——这里它作用于文档中的每个元素。 text() 是一个选择器,它匹配作为上下文节点的所有子节点的所有文本节点——它返回一个节点集。 内部[] 是一个条件,作用于该节点集中的每个节点——这里是每个单独的文本节点。每个单独的文本节点都是括号中任何路径的起点,也可以在括号内明确称为.。如果它所操作的任何单个节点与括号内的条件匹配,则它匹配。 contains 是一个对字符串进行操作的函数。这里传递了一个单独的文本节点 (.)。由于它是单独传递&lt;Comment&gt; 标记中的第二个文本节点,因此它将看到'ABC' 字符串并能够匹配它。

【讨论】:

太棒了,我有点 xpath 菜鸟,所以让我明白这个,text() 是一个函数,它接受表达式 contains(.,'ABC'),有没有机会你可以解释一下所以我不会再做这种愚蠢的事情了;) 我已经编辑了我的答案以提供一个很长的解释。我自己对 XPath 的了解并不多——我只是做了一些试验,直到我偶然发现了这种组合。一旦我有了一个可行的组合,我就猜测发生了什么,然后查看XPath standard 以确认我的想法并写下解释。 您如何将其设为不区分大小写的搜索? 我知道这是一个旧线程,但任何人都可以评论是否存在根本差异,最好在 Ken Bloom 和//*[contains(., 'ABC')] 给出的答案之间提供一些简单的测试用例。我一直使用 Mike Milkin 给出的模式,认为它更合适,但只是在当前上下文中执行 contains 似乎实际上是我更经常想要的。 ...//*[text()[contains(.,'ABC')]] 表示text()[contains(.,'ABC')]true 的任何元素。 text()[contains(.,'ABC')] 是上下文节点的所有文本节点子节点的节点集,其中 contains(.,'ABC')true。由于text()[contains(.,'ABC')] 是一个节点集,它被boolean() 函数转换为布尔值。对于节点集,boolean() 如果不为空,则返回 true【参考方案2】:

XML 文档:

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>

XPath 表达式:

//*[contains(text(), 'ABC')]

//* 匹配root node 中的任何descendant element。也就是说,除了根节点之外的任何元素。

[...] 是一个predicate,它过滤节点集。它返回 ...true 的节点:

谓词过滤节点集 [...] 以产生新的节点集。对于要过滤的节点集中的每个节点, PredicateExpr 被评估 [...];如果 PredicateExpr 对该节点的计算结果为真,则该节点包含在新节点集中;否则不包括在内。

contains('haystack', 'needle') 返回true 如果haystack contains needle:

函数:布尔包含(字符串,字符串)

如果第一个参数字符串包含第二个参数字符串,则 contains 函数返回 true,否则返回 false。

但是contains() 将字符串作为其第一个参数。它通过了节点。要处理作为第一个参数传递的每个节点或节点集是 converted 到 string() 函数的字符串:

参数被转换为字符串类型,就像调用字符串函数一样。

string() 函数返回string-value of the first node:

通过返回节点集中文档顺序中第一个节点的字符串值,将节点集转换为字符串。如果节点集为空,则返回一个空字符串。

string-value 的element node:

元素节点的字符串值是该元素节点的所有文本节点后代的字符串值按文档顺序串联。

string-value 的 text node:

文本节点的字符串值是字符数据。

所以,基本上string-value 是节点中包含的所有文本(所有后代文本节点的连接)。

text() 是匹配任何文本节点的节点测试:

节点测试 text() 对任何文本节点都为真。例如,child::text() 将选择上下文节点的文本节点子节点。

话虽如此,//*[contains(text(), 'ABC')] 匹配任何元素(根节点除外),其第一个文本节点包含ABC。由于text() 返回一个节点集,其中包含上下文节点的所有子文本节点(相对于其计算表达式)。但是contains() 只接受第一个。所以对于上面的文档,路径匹配Street元素。

以下表达式//*[text()[contains(., 'ABC')]] 匹配任何元素(根节点除外),该元素至少有一个包含ABC 的子文本节点。 . 代表上下文节点。在这种情况下,它是除根节点之外的任何元素的子文本节点。所以对于上面的文档,路径匹配StreetComment元素。

现在,//*[contains(., 'ABC')] 匹配包含ABC(在后代文本节点的串联中)的任何元素(根节点除外)。对于上面的文档,它匹配HomeAddrStreetComment 元素。因此,//*[contains(., 'BLAH ABC')] 匹配 HomeAddrComment 元素。

【讨论】:

与接受的答案一样,此答案仅与 XPath 1.0 有关。 XPath 2.0(2007 年发布)及更高版本的情况有所不同。【参考方案3】:

[contains(text(),'')] 只返回真或假。它不会返回任何元素结果。

【讨论】:

如果我有 ' ' 或 ' ' 这将不起作用,我们如何修剪? contains(text(),'JB-') 不行! conatains两个字符串 作为参数 - contains(**string**, **string**)! text() 不是字符串,是一个函数!【参考方案4】:
//*[text()='ABC'] 

返回

<street>ABC</street>
<comment>BLAH BLAH BLAH <br><br>ABC</comment>

【讨论】:

在对已有 5 个已有答案的 9 年老问题添加答案时,指出您的答案所针对的问题的独特新方面非常重要。 我发布的答案很简单。所以想分享一下,可能对我这样的初学者有所帮助。【参考方案5】:

接受的答案也将返回所有父节点。即使字符串在 :

之后,也只获取带有 ABC 的实际节点
//*[text()[contains(.,'ABC')]]/text()[contains(.,"ABC")]

【讨论】:

如果有人想获取文本节点的父元素;使用/.. 进行后修复查询,如下所示://*[text()[contains(.,'ABC')]]/text()[contains(.,"ABC")]/.. 谢谢! @罗杰【参考方案6】:

这是匹配包含给定文本字符串的节点的另一种方法。先查询文本节点本身,然后获取父节点:

//text()[contains(., "ABC")]/..

对我来说,这很容易阅读和理解。

【讨论】:

【参考方案7】:

这是主题问题的最佳答案:

//*[text()[contains(.,'ABC')]]/text()[contains(.,"ABC")]

一个例子: example case

Xpath 获取bon dua madam

//h3[text()='Contact Information']/parent::div/following-sibling::div/p[text()[contains(.,'bon dua madam')]]/text()[contains(.,'bon dua madam')]

【讨论】:

【参考方案8】:

我花了一点时间,但终于想通了。包含以下一些文本的自定义 xpath 非常适合我。

//a[contains(text(),'JB-')]

【讨论】:

contains(text(),'JB-') 不行! conatains两个字符串 作为参数 - contains(**string**, **string**)! text() 不是字符串,是一个函数!

以上是关于与具有多个文本子节点的节点一起使用时,XPath contains(text(),'some string') 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

如果节点与 Xpath 一起存在,则修改属性?

更新与 Xpath 查询匹配的多个 XML 节点 C#

XPath 在多个级别具有多个条件?

使用 XPath 选择具有具体类的第一个节点

Python爬虫编程思想(43):XPath实战:按序选择节点

如何使用 XPath 忽略命名空间