java怎么遍历document
- 后端开发
- 2025-08-10
- 7
基础认知体系构建
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兼容性 | (差) | (差) | (差) | (极优) |
| 修改文档能力 | (强) | (弱) | (中) |