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

Mik*_*kin 235 xpath dom4j

我有一个小问题,Xpath包含dom4j ...

让我们说我的XML是

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>
Run Code Online (Sandbox Code Playgroud)

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

所以我需要写的xpath就是

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

然而,这不是Dom4j返回的......这是dom4j问题还是我理解xpath如何工作.因为该查询仅返回Street元素而不返回Comment元素.

DOM使Comment元素成为具有四个标记2的复合元素

[Text = 'XYZ'][BR][BR][Text = 'ABC'] 
Run Code Online (Sandbox Code Playgroud)

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

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

//*[contains(text(),'ABC')]
Run Code Online (Sandbox Code Playgroud)

是否有任何人知道XPath查询会只返回元素<Street/><Comment/>

Ken*_*oom 647

<Comment>标签包含两个文本节点和两个<br>儿童的节点.

你的xpath表达式是

//*[contains(text(),'ABC')]
Run Code Online (Sandbox Code Playgroud)

打破这个,

  1. * 是一个匹配任何元素(即标记)的选择器 - 它返回一个节点集.
  2. []是一个条件,在该节点组中的每个单独的节点上运行.如果它操作的任何单个节点与括号内的条件匹配,则匹配.
  3. text()是一个选择器,它匹配作为上下文节点的子节点的所有文本节点 - 它返回一个节点集.
  4. contains是一个对字符串进行操作的函数.如果传递了节点集,则通过返回节点集中节点的字符串值将节点集转换为字符串,该节点集是文档顺序中的第一个节点集.因此,它只能匹配<Comment>元素中的第一个文本节点- 即BLAH BLAH BLAH.由于这不匹配,您不会得到<Comment>结果.

您需要将其更改为

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

  • 我编辑了我的答案,提供了一个很长的解释.我自己对XPath的了解并不多 - 我只是尝试了一下,直到我偶然发现了这个组合.一旦我有一个工作组合,我猜测发生了什么,并查看[XPath标准](http://www.w3.org/TR/xpath/),以确认我的想法发生并写下说明. (27认同)
  • 你会如何使这个不区分大小写的搜索? (2认同)
  • ...`//*[text()[contains(.,'ABC')]]` 表示 `text()[contains(.,'ABC')]` 为 [`true`]( https://www.w3.org/TR/xpath-10/#predicates)。`text()[contains(.,'ABC')]` 是 `contains(.,'ABC')` 为 `true` 的上下文节点的所有文本节点子节点的节点集。由于 `text()[contains(.,'ABC')]` 是一个节点集,因此通过 [`boolean()`] 将其转换为布尔值(https://www.w3.org/TR/xpath-10 /#function-boolean) 函数。对于节点集,如果“boolean()”不为空,则返回“true”。 (2认同)

kjh*_*hes 23

现代答案涵盖 XPath 1.0 与 XPath 2.0+ 行为......

这个 XPath,

//*[contains(text(),'ABC')]
Run Code Online (Sandbox Code Playgroud)

XPath 1.0 和更高版本的 XPath (2.0+) 的行为有所不同。

常见行为

  • //*选择文档中的所有元素。
  • []根据其中表达的谓词过滤这些元素。
  • contains(string, substring)谓词内将过滤那些其子字符串substring为的元素。string

XPath 1.0 行为

  • contains(arg1, substring):如果第一个参数的计算结果为节点集,则通过获取节点集中第一个节点的字符串值,将节点集contains()转换为字符串。(如果是,则只会考虑所有匹配节点中的第一个。)如果您发现这种情况很奇怪,那么您并不孤单。arg1text()textcontains()
  • 对于//*[contains(text(),'ABC')]该节点集将是文档中每个元素的所有子文本节点。
  • 由于仅使用第一个文本节点子节点'ABC',因此违反了对所有子文本节点进行子字符串包含测试的期望。
  • 对于任何不熟悉上述转换规则的人来说,这都会导致反直觉的结果。

XPath 1.0在线示例显示仅'ABC'选择了一项。

XPath 2.0+ 行为

  • contains(arg1, substring)使用多个项目的序列作为第一个参数进行调用是错误的。
  • 这纠正了上述 XPath 1.0 中的反直觉行为。

XPath 2.0 在线示例显示了由于 XPath 2.0+ 特有的转换错误而导致的典型错误消息。

常见解决方案

  1. 如果您希望包含后代元素(超出子元素),请将元素的字符串值作为单个字符串进行测试,而不是子文本节点的各个字符串值,此 XPath,

    //*[contains(.,'ABC')]
    
    Run Code Online (Sandbox Code Playgroud)

    选择您的目标StreetComment元素以及它们的AddrHome祖先元素,因为它们也具有'ABC'其字符串值的子字符串。

    在线示例显示祖先也被选择。

  2. 如果您希望排除后代元素(超出子元素),则此 XPath,

    //*[text()[contains(.,'ABC')]]
    
    Run Code Online (Sandbox Code Playgroud)

    仅选择您的目标StreetComment因为只有这些元素具有其字符串值包含子字符串的文本节点子'ABC'节点。对于所有版本的 XPath 都是如此

    在线示例仅显示Street并被Comment选中。


x-y*_*uri 14

XML 文档:

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>
Run Code Online (Sandbox Code Playgroud)

XPath 表达式:

//*[contains(text(), 'ABC')]
Run Code Online (Sandbox Code Playgroud)

//*匹配任何后裔元件的的根节点。也就是说,除了根节点之外的任何元素。

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

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

contains('haystack', 'needle')true如果haystack 包含则 返回needle

功能:布尔值包含(字符串,字符串)

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

但是contains()需要一个字符串作为它的第一个参数。它通过了节点。为了处理作为第一个参数传递的每个节点或节点集都被函数转换为字符串string()

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

string()函数返回string-value第一个节点

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

string-value一个的元素节点

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

string-value一个的文本节点

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

因此,基本上string-value是节点中包含的所有文本(所有后代文本节点的串联)。

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

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

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

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

现在,//*[contains(., 'ABC')]匹配包含ABC(在后代文本节点的串联中)的任何元素(但根节点)。对于上面它相匹配的文档Home,所述AddrStreet,和Comment元件。这样,//*[contains(., 'BLAH ABC')]匹配HomeAddr,和Comment元件。

  • 与已接受的答案一样,该答案仅与 XPath 1.0 相关。XPath 2.0(2007 年发布)及更高版本的情况有所不同。 (3认同)

小智 6

[contains(text(),'')]只返回true或false.它不会返回任何元素结果.


Rog*_*ana 5

接受的答案也将返回所有父节点。即使字符串位于之后,也仅获取具有 ABC 的实际节点

//*[text()[contains(.,'ABC')]]/text()[contains(.,"ABC")]
Run Code Online (Sandbox Code Playgroud)


lea*_*Fun 5

//*[text()='ABC'] 
Run Code Online (Sandbox Code Playgroud)

回报

<street>ABC</street>
<comment>BLAH BLAH BLAH <br><br>ABC</comment>
Run Code Online (Sandbox Code Playgroud)

  • 当为一个有五个现有答案的九年前的问题添加答案时,指出您的答案所解决的问题的独特新方面非常重要。 (6认同)
  • 我发布的答案非常简单。所以想分享一下,或许对像我这样的初学者有帮助。 (2认同)