在XML和HTML文档的解析工作中,我们常常需要根据节点的子元素特征来筛选目标节点,其中选择不包含某个特定子元素的节点是非常常见的需求。XPath作为专门用于定位XML和HTML文档节点的语言,提供了多种语法来实现这个需求,不同的写法适用于不同的场景,掌握这些方法能大幅提升文档解析的效率。

核心实现方法
1. 使用not()函数结合子元素判断
最常用的方法是借助XPath的not()函数,配合子元素定位表达式来判断当前节点是否不包含目标子元素。基本语法结构为://父节点标签[not(子节点标签)],这个表达式会匹配所有父节点标签下,不存在指定子节点标签的直接子节点的节点。
如果需要判断所有层级的子元素(不仅是直接子元素),可以使用//父节点标签[not(.//子节点标签)],其中.//表示从当前节点出发查找所有后代节点。
2. 使用count()函数判断子元素数量
另一种思路是先统计目标子元素的数量,当数量为0时就说明不包含该子元素。语法结构为://父节点标签[count(子节点标签) = 0],这种写法的逻辑更直观,和not()函数的效果一致,适合对XPath函数不太熟悉的开发者使用。
实际代码示例
假设我们有如下的XML文档结构,需要选择所有不包含<price>子元素的<book>节点:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="1">
<title>XML入门教程</title>
<author>张三</author>
<price>59.9</price>
</book>
<book id="2">
<title>XPath实战指南</title>
<author>李四</author>
</book>
<book id="3">
<title>HTML解析技巧</title>
</book>
</bookstore>
使用not()函数的XPath表达式如下:
//book[not(price)]
使用count()函数的XPath表达式如下:
//book[count(price) = 0]
上述两个表达式都会匹配到id为2和id为3的<book>节点,这两个节点都不包含<price>子元素。
如果是HTML文档的场景,比如要选择所有不包含<img>子元素的<div>标签,表达式可以写成:
//div[not(.//img)]
这个表达式会匹配所有后代节点中都没有<img>标签的<div>节点。
常见注意事项
- 注意区分直接子元素和后代元素:
not(子节点标签)只判断直接子元素,not(.//子节点标签)判断所有后代元素,需要根据实际需求选择。 - 子元素标签名要准确:如果文档中有命名空间,需要带上对应的命名空间前缀,否则会匹配不到目标子元素。
- 避免冗余写法:不需要同时用
not()和count() = 0,两种写法选一种即可,冗余写法会降低表达式的可读性。
扩展场景:排除包含特定属性子元素的节点
如果需要排除包含某个特定属性值的子元素的节点,可以结合属性判断一起使用,比如选择不包含<price>子元素且price值大于50的<book>节点,表达式可以写成:
//book[not(price) or price > 50]
这里需要注意XPath中大于号的转义,直接使用>即可,解析器会正确识别。