当前位置:首页 > 后端开发 > 正文

java怎么遍历document

使用 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()预读下一个事件,适合复杂条件判断场景。

java怎么遍历document  第1张


常见问题解决方案库

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兼容性 (差) (差) (差) (极优)
修改文档能力 (强) (弱) (中)
0