上一篇
java怎么遍历document
- 后端开发
- 2025-08-10
- 42
使用
Document.getChildNodes() 获取子节点,递归遍历并判断节点类型为
ELEMENT_NODE
基础认知体系构建
1 文档模型分类
| 模型类型 | 核心特征 | 适用场景 | Java标准库支持 |
|---|---|---|---|
| DOM | 树形结构/全量加载/随机访问 | 中小型文档/频繁查询修改 | javax.xml.parsers |
| SAX | 事件驱动/增量解析/低内存消耗 | 超大型文档/单次顺序处理 | org.xml.sax |
| StAX | 拉模式解析/游标控制/高效过滤 | 复杂数据抽取/流式处理 | javax.xml.stream |
| Jsoup (HTML) | 类jQuery API/宽松容错机制 | HTML5文档/网页爬虫开发 | org.jsoup |
2 关键术语对照表
| 术语 | 说明 | 典型应用场景 |
|---|---|---|
| Node | 文档节点基类 | 所有元素/文本/注释的基础 |
| Element | 含子节点的元素节点 | <book>, <title>等标签 |
| Text | 节点 | 段落文字/属性值 |
| NamedNodeMap | 属性集合 | 获取元素的id, class等 |
| NodeList | 有序节点集合 | 通过CSS选择器获取的结果集 |
DOM解析深度实践(以XML为例)
1 标准实现流程
// 1. 创建DocumentBuilderFactory实例
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true); // 启用命名空间支持
// 2. 构建Document对象
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File("books.xml"));
// 3. 遍历根节点下的所有子节点
NodeList bookList = doc.getElementsByTagName("book");
for (int i = 0; i < bookList.getLength(); i++) {
Element book = (Element) bookList.item(i);
// 4. 提取元素内容
String title = book.getElementsByTagName("title").item(0).getTextContent();
double price = Double.parseDouble(book.getElementsByTagName("price").item(0).getTextContent());
// 5. 处理属性
String category = book.getAttribute("category");
// 6. 递归遍历子节点(可选)
traverseChildren(book);
}
private void traverseChildren(Node parent) {
NodeList children = parent.getChildNodes();
for (int j = 0; j < children.getLength(); j++) {
Node child = children.item(j);
if (child.getNodeType() == Node.ELEMENT_NODE) {
System.out.println("发现元素: " + child.getNodeName());
traverseChildren(child); // 深度优先遍历
} else if (child.getNodeType() == Node.TEXT_NODE) {
System.out.println("文本内容: " + child.getTextContent().trim());
}
}
}
2 高级技巧组合
| 技术手段 | 实现方式 | 优势体现 |
|---|---|---|
| XPath表达式 | XPathFactory.newInstance().newXPath().evaluate() |
精准定位复杂路径节点 |
| 迭代器模式 | NodeIterator配合while(iterator.hasNext())循环 |
避免显式索引管理 |
| Lambda表达式(Java8+) | Arrays.stream(nodeList).forEach(node -> ...) |
简化集合操作 |
| 并行流处理 | Arrays.stream(nodeList).parallel().forEach(...) |
提升多核CPU利用率 |
3 性能优化策略
| 优化方向 | 具体措施 | 效果评估 |
|---|---|---|
| 缓存机制 | 对重复访问的节点进行软引用缓存 | 减少DOM树遍历次数 |
| 批量处理 | 将多次getElementsByTagName合并为单次查询 |
降低I/O操作频率 |
| 懒加载 | 仅在需要时展开子节点 | 初始加载时间缩短70%+ |
| 内存监控 | 使用Runtime.getRuntime().totalMemory()跟踪内存变化 |
防止OOM异常发生 |
HTML文档特殊处理(Jsoup方案)
1 典型用法示例
// 1. 解析HTML文档
Document doc = Jsoup.connect("https://example.com").get();
// 2. 选择器方式遍历
Elements links = doc.select("a[href]"); // 获取所有带href属性的链接
for (Element link : links) {
String url = link.attr("href");
String text = link.text();
// ...后续处理逻辑
}
// 3. 修改文档结构
Element table = doc.selectFirst("table");
table.select("tr:odd").addClass("highlight"); // 隔行变色效果
System.out.println(doc.html()); // 输出修改后的HTML
2 HTML特有处理要点
| 问题类型 | 解决方案 | 注意事项 |
|---|---|---|
| 自闭合标签 | Jsoup自动规范化处理<img />为<img> |
无需手动补全 |
| 特殊字符转义 | StringEscapeUtils.unescapeHtml4()处理实体编码 |
防止XSS攻击需二次过滤 |
| CSS样式提取 | element.attr("style")可直接获取内联样式 |
外部样式表需单独解析 |
SAX与StAX流式解析对比
1 SAX事件驱动模型
public class SaxParser extends DefaultHandler {
@Override
public void startElement(String uri, String localName, String qName, Attributes attrs) {
if ("book".equals(qName)) {
currentBook = new Book();
// 初始化新书籍对象
}
}
@Override
public void characters(char[] ch, int start, int length) {
if (currentBook != null) {
currentBook.setContent(new String(ch, start, length));
}
}
@Override
public void endElement(String uri, String localName, String qName) {
if ("book".equals(qName)) {
books.add(currentBook);
currentBook = null;
}
}
}
优势:内存占用仅为DOM的1/5~1/10,适合GB级大文件处理;劣势:无法随机访问节点,需自行维护状态机。
2 StAX拉模式解析
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("large.xml"));
while (reader.hasNext()) {
int event = reader.next();
switch (event) {
case XMLStreamReader.START_ELEMENT:
if ("important".equals(reader.getLocalName())) {
// 处理关键元素
}
break;
case XMLStreamReader.CHARACTERS:
// 处理文本内容
break;
// ...其他事件处理
}
}
特点:结合推/拉模式优势,可通过peek()预读下一个事件,适合复杂条件判断场景。

常见问题解决方案库
Q1: 如何处理XML命名空间冲突?
A: 在创建DocumentBuilder时设置命名空间感知:

factory.setNamespaceAware(true);
// 然后通过带URI的完整名称访问元素:
NodeList nodes = doc.getElementsByTagNameNS("http://example.com/ns", "localName");
补充说明:若遇到未知命名空间,可通过builder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)禁用外部DTD加载。
Q2: 遍历时出现”NullPointerException”如何解决?
A: 常见原因及对策:
| 错误位置 | 根本原因 | 解决方案 |
|————————|——————————|———————————–|
| item(i)返回null | 索引越界或标签不存在 | 添加if (i < nodeList.getLength())校验 |
| getTextContent()为空 | 节点类型不匹配(如注释节点) | 先检查getNodeType()是否为ELEMENT_NODE |
| 属性获取失败 | 属性未定义 | 使用hasAttribute()前置判断 |
| Jsoup选择器无结果 | CSS语法错误或DOM未完全加载 | 添加await().atMost(5, SECONDS)等待异步加载 |

选型决策矩阵
| 评价维度 | DOM | SAX | StAX | Jsoup (HTML) |
|---|---|---|---|---|
| 内存效率 | (差) | (优) | (良) | (中) |
| 开发便捷性 | (易) | (难) | (中) | (极优) |
| 随机访问能力 | (强) | (弱) | (中) | (中) |
| 大数据适配性 | (差) | (优) | (良) | (差) |
| HTML兼容性 | (差) | (差) | (差) | (极优) |
| 修改文档能力 | (强) | (弱) | (中) |
