XML文档中的空白节点指的是仅包含空格、换行、制表符等空白字符的文本节点,这类节点在解析时常常会被误判为有效数据,影响后续的业务逻辑处理。了解空白节点的产生原因和处理方式,是XML相关开发中的必备技能。

空白节点的产生原因
XML文档为了提升可读性,通常会在标签之间添加换行、缩进等空白字符,这些字符在解析时会被解析器识别为独立的文本节点。比如下面的XML示例,book标签和title标签之间的换行和缩进就会生成空白节点:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book>
<title>XML开发指南</title>
<price>59.9</price>
</book>
</bookstore>
DOM解析方式处理空白节点
DOM解析会将整个XML文档加载为树形结构,默认情况下会保留所有空白节点,我们可以通过两种方式处理这些节点。
方法一:解析时忽略空白节点
在使用DOM解析器时,可以开启忽略空白节点的配置,部分解析器支持该特性。以Java的DOM解析为例,代码如下:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
public class DomWhitespaceHandle {
public static void main(String[] args) throws Exception {
// 创建解析器工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 开启忽略空白节点配置
factory.setIgnoringElementContentWhitespace(true);
DocumentBuilder builder = factory.newDocumentBuilder();
// 解析XML文件
Document document = builder.parse("book.xml");
// 获取所有book节点,此时不会包含空白节点
NodeList bookNodes = document.getElementsByTagName("book");
System.out.println("book节点数量:" + bookNodes.getLength());
}
}
方法二:解析后过滤空白节点
如果解析器不支持忽略空白节点的配置,可以在获取到节点后手动过滤。空白节点的判断标准是节点类型为文本节点,且trim后长度为0:
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class DomFilterWhitespace {
// 过滤指定节点下的空白子节点
public static void removeWhitespace(Node node) {
NodeList childNodes = node.getChildNodes();
for (int i = childNodes.getLength() - 1; i >= 0; i--) {
Node child = childNodes.item(i);
// 判断是否为空白文本节点
if (child.getNodeType() == Node.TEXT_NODE && child.getTextContent().trim().isEmpty()) {
node.removeChild(child);
} else if (child.hasChildNodes()) {
// 递归处理子节点
removeWhitespace(child);
}
}
}
}
SAX解析方式处理空白节点
SAX解析是事件驱动的流式解析,不会一次性加载整个文档,处理空白节点需要在characters方法中判断。当解析到文本时,先对内容做trim处理,如果为空则跳过:
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
public class SaxWhitespaceHandle extends DefaultHandler {
private StringBuilder content = new StringBuilder();
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
content.append(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
String text = content.toString().trim();
// 过滤空白内容
if (!text.isEmpty()) {
System.out.println(qName + "的内容:" + text);
}
content.setLength(0);
}
public static void main(String[] args) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse("book.xml", new SaxWhitespaceHandle());
}
}
不同处理方式的适用场景
我们可以通过下面的表格对比不同处理方式的优缺点,选择适合自己场景的方案:
| 处理方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| DOM解析时忽略空白 | 配置简单,无需手动处理 | 依赖解析器支持,部分场景不生效 | 解析器支持该特性,需要操作整个文档树 |
| DOM解析后过滤 | 兼容性好,所有DOM解析场景都可用 | 需要遍历节点,性能略有损耗 | 解析器不支持忽略空白配置,需要操作文档树 |
| SAX解析过滤 | 内存占用低,适合大文件 | 无法回溯节点,只能顺序处理 | 处理大体积XML文件,只需提取部分数据 |
注意事项
- 不是所有空白节点都需要过滤,比如
pre标签内的空白、需要保留格式的文本内容,过滤后会导致数据丢失。 - 不同解析器对空白节点的识别规则可能有差异,测试时需要验证处理逻辑是否符合预期。
- 如果XML文档中混合了有意义空格和无意义空白,需要结合业务规则做更精细的判断,不能一概过滤。