对接一个老接口,返回的是 XML。项目里已经有依赖,同事说用 DOM 就行,我看报文有十几兆,DOM 一次全加载进内存,估计当场就 OOM。解析方式选不对,小文件跑得欢,大文件直接崩。

DOM:树状模型,能读能改

DocumentBuilder 把整个文档构造成一棵树,节点可以随便遍历、修改,最后还能写回文件。缺点是内存占用和文档大小成正比,几十兆以上就危险。配置文件和几百 KB 的响应体用它最舒服。

SAX:事件驱动,边读边抛

读到开始标签、结束标签就回调你的方法,内存占用恒定,再大的文件也扛得住。缺点是要自己维护状态,代码写起来绕,而且只能顺序读一遍。

StAX:拉模式,最推荐

  • 主动控制:用 XMLStreamReader 自己调 next(),遇到想要的事件再处理。
  • 省内存:底层是流式,处理大文件不用怕 OOM。
  • 能读能写:一边读一边写,做 XML 到 JSON 的转换很顺手。

一段 DOM 的骨架代码

DocumentBuilderFactory f = DocumentBuilderFactory.newInstance(); DocumentBuilder b = f.newDocumentBuilder(); Document doc = b.parse(new File(path)); NodeList list = doc.getElementsByTagName(itemTag); for (int i = 0; i < list.getLength(); i++) { Element e = (Element) list.item(i); System.out.println(e.getAttribute(idAttr)); }

解析时记得关掉外部实体,不然碰上恶意 DTD 会有 XXE 风险,factory.setFeature 里把外部实体特性设为 false。

三种没有绝对好坏。文件小、要改内容,用 DOM 图省事;文件大、只读不写,用 SAX 或 StAX 保住内存。生产里读大 XML 我一般直接上 StAX,配合对象映射框架,比手写解析省一半代码。