各位代码建筑师们!今天我们要玩一个把XML变成内存乐高城堡的游戏——DOM解析!和SAX那种"边看监控边破案"的刺激不同,DOM就像把整个乐高说明书一次性倒进大脑,然后慢慢拼装(内存:你不要过来啊!)
一、DOM原理:XML的"克隆人战争"
想象你要复制整个迪士尼乐园:
-
全量加载术
把XML文件整个吞进内存,变成一颗节点树(就像把城堡图纸转成3D模型) -
随机访问特权
可以随时瞬移到任意角落:“我要修改第三块砖的颜色!”(而SAX只能从头看到尾) -
修改超能力
支持增删改查,像玩《模拟人生》一样随意改造XML世界
二、实战演练:用DOM搭建"程序员主题乐园"
项目蓝图(programmer_park.xml):
<主题乐园 名称="996快乐谷">
<区域 类型="代码深渊" geohash="wx4g0b1">
<设施 id="1">
<名称>无限续杯咖啡厅</名称>
<危险等级>★★★★☆</危险等级>
</设施>
<设施 id="2">
<名称>需求变更过山车</名称>
<危险等级>★★★★★</危险等级>
</设施>
</区域>
</主题乐园>
建筑师工具包(DomArchitect.java):
import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
public class DomArchitect {
public static void main(String[] args) throws Exception {
// 装载整个乐园到内存
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document park = builder.parse("programmer_park.xml");
// 打印所有危险设施
NodeList rides = park.getElementsByTagName("设施");
System.out.println("⚠️ 高危设施列表:");
for (int i=0; i<rides.getLength(); i++) {
Element ride = (Element) rides.item(i);
String name = ride.getElementsByTagName("名称").item(0).getTextContent();
String level = ride.getElementsByTagName("危险等级").item(0).getTextContent();
System.out.println(name + " | 危险指数:" + level);
}
// 新增一个夺命设施
Element newRide = park.createElement("设施");
newRide.setAttribute("id", "3");
Element name = park.createElement("名称");
name.appendChild(park.createTextNode("Deadline蹦极台"));
Element level = park.createElement("危险等级");
level.appendChild(park.createTextNode("★★★★★★")); // 突破五星!
newRide.appendChild(name);
newRide.appendChild(level);
park.getDocumentElement().getFirstChild().appendChild(newRide);
// 保存修改后的乐园(小心内存泄漏!)
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(new DOMSource(park),
new StreamResult("programmer_park_modified.xml"));
}
}
运行结果:
⚠️ 高危设施列表:
无限续杯咖啡厅 | 危险指数:★★★★☆
需求变更过山车 | 危险指数:★★★★★
(生成的新XML会多出一个"Deadline蹦极台",危险指数突破天际!)
三、DOM vs SAX:建筑师与侦探的巅峰对决
DOM建筑师 🏗️ | SAX侦探 🕵️♂️ | |
---|---|---|
内存消耗 | 需要搬来整个建材市场(全量加载) | 只带侦探工具包(流式处理) |
操作方式 | 可以随意拆墙装修(随机修改) | 只能做现场记录(只读) |
响应速度 | 装修前要先运材料(初始化慢) | 到达现场立即开工(启动快) |
适用场景 | 需要改结构的精致小别墅 | 快速搜查犯罪现场的超大仓库 |
四、DOM操作三大"骚操作"
-
XPath闪电定位
用XPath直接空降到指定节点,像使用传送门:XPath xpath = XPathFactory.newInstance().newXPath(); Node node = (Node) xpath.evaluate("//设施[名称='需求变更过山车']", park, XPathConstants.NODE);
-
属性隐身术
动态修改geohash坐标,让设施"瞬间移动":Element area = (Element) park.getElementsByTagName("区域").item(0); area.setAttribute("geohash", "wx4g0b9"); // 从深渊传送到厕所
-
节点克隆大法
复制过山车并改名,省时省力:Node clonedRide = rides.item(1).cloneNode(true); ((Element)clonedRide).setAttribute("id", "4"); clonedRide.getChildNodes().item(0).setTextContent("需求复活过山车");
五、DOM的致命陷阱
-
内存黑洞
加载1GB的XML文件 ≈ 在内存里造航空母舰(小心OOM空袭!) -
空白节点鬼打墙
XML中的换行符会被视为Text节点,遍历时可能踩坑:// 错误示范:直接取第一个子节点可能是空白文本节点! // Element name = (Element) ride.getFirstChild(); // 正确姿势:过滤文本节点 NodeList children = ride.getChildNodes(); for (int i=0; i<children.getLength(); i++) { if (children.item(i).getNodeType() == Node.ELEMENT_NODE) { Element child = (Element) children.item(i); // 处理真实节点 } }
-
线程安全惊魂
Document对象不是线程安全的!多个线程同时装修会拆了你的乐高城堡。
六、DOM哲学:内存即世界
- 每个Element节点都是乐高积木
- 每个Text节点都是积木上的贴纸
- 每个Attribute都是积木的卡扣设计
- 而内存溢出…是你野心太大想造死星的下场