1

使用 XPath (.NET),我试图选择所有不包含任何文本节点的节点。

鉴于此文件:

<root>
  <node1>
    <node1a>Node 1A</node1a>
  </node1>
  <node2>Node 2</node2>
  <node3>
    <node3a>Node 3A</node3a>
    <node3b></node3b>
  </node3>
  <node4></node4>
  <node5>
    <node5A></node5A>
  </node5>
</root>

我很想得到节点:

<node3b></node3b>

<node4></node4>

<node5>
  <node5A></node5A>
</node5>

请注意,重叠的子树是合并的,因此不应单独返回 node5A。

我希望这可以解决问题,但是由于某种原因(当有人指出时这可能很明显),它不会:

//*[count(//text()) = 0]

注意:我正在使用XPath 测试器进行测试。

4

4 回答 4

2

Arg ...就在发布时,解决方案突然出现:

//*[count(.//text()) = 0]

解释:条件count(//text()) = 0从根开始计算所有文本节点,总是大于零。要从当前节点开始计数,我需要在点前面加上前缀:count(.//text()) = 0

请注意,@jvverde 正确地指出节点可以在结果集中多次出现。所以这个表达式与我提到的条件不完全匹配,因为 node5A 在那里两次:

<node3b></node3b>

<node4></node4>

<node5>
  <node5A></node5A>
</node5>

<node5A></node5A>
于 2013-08-26T07:07:07.093 回答
1

您也可以使用//*[.='']空元素应该具有空字符串值。

于 2013-08-26T08:06:46.350 回答
1

假设您的结果示例确实是您想要的(这不完全符合标题中的陈述)上面的建议

//*[count(.//text()) = 0]

或首选方式

//*[not(.//text())]

不起作用,因为结果不是您所期望的

<node3b />
<node4 />
<node5>
  <node5A />
</node5>
<node5A /> <!-- this node is not present in your example -->

如果您想要的是没有任何文本节点的所有子树不包含在其他结果子树中,那么解决方案就是这个

//*[not(.//text())][not(ancestor::*[not(.//text())])]

第二个谓词从结果中删除所有至少有一个祖先的节点已经包含在结果中

于 2013-08-26T09:33:37.467 回答
0

你也可以使用更简单易读的

//*[not(.//text())]

或者如果您愿意,可以替换not(...)为。empty(...)

两者都已经优化,因此即使是简单的 XPath 实现也应该能够以“快速失败”的方式实现它们(找到一个文本节点,将谓词评估为假)。

于 2013-08-26T09:02:40.927 回答