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


258

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

可以说我的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')]

有谁知道xpath查询会只返回Elements <Street/><Comment/>吗?


据我所知,//*[contains(text(),'ABC')]仅返回该<Street>元素。它不返回<Street>或的任何祖先<Comment>
肯·布鲁姆

Answers:


706

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

您的xpath表达式是

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

为了解决这个问题,

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

您需要将其更改为

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

1
真棒,我有点xpath新手,所以让我明白这一点,text()是一个函数,它使用表达式contains(。,'ABC'),您是否有机会可以解释一下,所以我不这样做又是愚蠢的东西;)
Mike Milkin 2010年

28
我已经对答案进行了编辑,以提供详细的解释。我本人对XPath并不十分了解-我做了一些实验,直到偶然发现了这种组合。一旦有了有效的组合,我就猜出了什么情况,并查看了XPath标准以确认我的想法并编写了说明。
肯·布鲁姆

2
您如何使它不区分大小写?
Zack

@Zack:请提出一个新问题。
user1129682 2015年

1
我知道这是一个旧线程,但是任何人都可以评论一下是否存在根本差异,最好在Ken Bloom和给出的答案之间使用一些简单的测试用例//*[contains(., 'ABC')]。我一直使用迈克·米尔金(Mike Milkin)给出的模式,认为它更合适,但是仅contains在当前环境下进行操作似乎实际上是我想要的。
knickum

7

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


如果我有''或''这行不通,怎么修剪?
shareef

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

6

XML文件:

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

XPath表达式:

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

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

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

谓词过滤节点集以产生新的节点集。对于节点集中要过滤的每个节点,都会对PredicateExpr进行评估。如果PredicateExpr对于该节点的评估结果为true,则该节点包含在新的节点集中;否则,不包括在内。

contains('haystack', 'needle')返回true是否haystack 包含 needle

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

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

但是contains()将字符串作为其第一个参数。它已通过节点。为了处理将作为第一个参数传递的每个节点或节点集通过以下函数转换为字符串string()

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

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

通过返回节点集中按文档顺序首先出现的节点的字符串值,可以将节点集转换为字符串。如果节点集为空,则返回一个空字符串。

string-value一个的元素节点

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

string-value一个的文本节点

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

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

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

节点测试text()对于任何文本节点都为true。例如,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元件。


0

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

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

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

0

接受的答案也将返回所有父节点。要仅使用ABC获取实际节点,即使该字符串在after之后

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

0
//*[text()='ABC'] 

退货

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

3
在对已有9个问题的已有9个现有问题的答案中添加答案时,指出您的答案要解决的问题有哪些独特之处是非常重要的。
杰森·艾勒

我发布的答案非常简单。这样的想法就像分享,可能会对像我这样的初学者有所帮助。
user3520544
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.