XML(可扩展标记语言)凭借结构清晰、跨平台兼容性强的特点,长期被用于数据交换、配置存储等场景,比如安卓应用的布局文件、Spring框架的配置文件都以XML格式存在。在实际开发中,我们常常需要读取XML内容、提取其中的数据,这就涉及到XML解析的相关技术。

XML解析的核心思路
XML解析的本质是把符合XML规范的文本字符串,转换为程序可以操作的数据结构,比如树结构、事件流等,之后再通过对应的API提取需要的信息。根据解析过程的实现方式,主流的解析方法可以分为两类:
- 基于树的解析:一次性把整个XML文档加载到内存中,构建成树形结构,之后可以随意遍历、修改节点,典型代表是DOM解析。
- 基于流的解析:逐行或者逐事件读取XML内容,不需要一次性加载全部文档,内存占用低,典型代表是SAX、StAX解析。
常见XML解析方式对比
不同的解析方式适用场景不同,开发者需要根据XML文件大小、操作需求选择合适的方法,以下是三种主流解析方式的对比:
| 解析方式 | 核心特点 | 内存占用 | 适用场景 |
|---|---|---|---|
| DOM解析 | 构建完整文档树,支持随机访问、修改节点 | 高(和文档大小正相关) | 小体积XML,需要频繁修改节点内容 |
| SAX解析 | 事件驱动,逐行读取触发回调,只读不写 | 低(仅保留当前节点信息) | 大体积XML,仅需读取提取数据 |
| StAX解析 | 主动拉取事件,可控性比SAX更强 | 低(可控制读取进度) | 大体积XML,需要灵活控制解析流程 |
Python语言下的XML解析实战
Python标准库内置了多个XML解析模块,不需要额外安装依赖就可以直接使用,下面分别给出不同解析方式的代码示例。
1. DOM解析示例
Python的xml.dom.minidom模块实现了DOM解析功能,适合处理小体积XML文件。
import xml.dom.minidom
# 待解析的XML内容,这里用字符串模拟,实际可以从文件读取
xml_content = """
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student id="1001">
<name>张三</name>
<age>20</age>
<major>计算机科学</major>
</student>
<student id="1002">
<name>李四</name>
<age>21</age>
<major>软件工程</major>
</student>
</students>
"""
# 解析XML字符串,也可以传入文件路径使用 parse("student.xml")
dom_tree = xml.dom.minidom.parseString(xml_content)
# 获取根节点
root = dom_tree.documentElement
# 获取所有student节点
student_nodes = root.getElementsByTagName("student")
for student in student_nodes:
# 获取student节点的id属性
student_id = student.getAttribute("id")
# 获取name子节点内容
name = student.getElementsByTagName("name")[0].childNodes[0].data
# 获取age子节点内容
age = student.getElementsByTagName("age")[0].childNodes[0].data
# 获取major子节点内容
major = student.getElementsByTagName("major")[0].childNodes[0].data
print(f"学生ID:{student_id},姓名:{name},年龄:{age},专业:{major}")
# 修改第一个学生的年龄
first_student = student_nodes[0]
age_node = first_student.getElementsByTagName("age")[0]
age_node.childNodes[0].data = "22"
print("修改后的第一个学生年龄:", age_node.childNodes[0].data)
# 将修改后的DOM树写回文件
with open("new_student.xml", "w", encoding="UTF-8") as f:
dom_tree.writexml(f, encoding="UTF-8")2. SAX解析示例
Python的xml.sax模块实现了SAX解析,适合处理大体积XML文件,通过自定义处理器处理解析事件。
import xml.sax
# 自定义SAX处理器,继承xml.sax.ContentHandler
class StudentHandler(xml.sax.ContentHandler):
def __init__(self):
self.current_tag = None
self.student_id = None
self.name = None
self.age = None
self.major = None
# 遇到元素开始标签时触发
def startElement(self, name, attrs):
self.current_tag = name
if name == "student":
# 获取student节点的id属性
self.student_id = attrs["id"]
# 遇到元素结束标签时触发
def endElement(self, name):
if name == "student":
# 一个student节点解析完成,输出信息
print(f"学生ID:{self.student_id},姓名:{self.name},年龄:{self.age},专业:{self.major}")
# 重置临时变量
self.student_id = None
self.name = None
self.age = None
self.major = None
self.current_tag = None
# 遇到元素内容时触发
def characters(self, content):
if self.current_tag == "name":
self.name = content
elif self.current_tag == "age":
self.age = content
elif self.current_tag == "major":
self.major = content
# 创建SAX解析器
parser = xml.sax.make_parser()
# 关闭命名空间处理
parser.setFeature(xml.sax.handler.feature_namespaces, 0)
# 设置自定义处理器
handler = StudentHandler()
parser.setContentHandler(handler)
# 解析XML文件,也可以用parseString方法解析字符串
parser.parse("student.xml")3. StAX解析示例
Python的xml.etree.ElementTree模块虽然不是标准StAX实现,但提供了类似的迭代解析功能,也可以高效处理大文件。
import xml.etree.ElementTree as ET
# 迭代解析XML文件,逐元素读取,内存占用低
for event, elem in ET.iterparse("student.xml", events=("start", "end")):
# 遇到元素结束事件时处理
if event == "end" and elem.tag == "student":
student_id = elem.attrib.get("id")
name = elem.find("name").text
age = elem.find("age").text
major = elem.find("major").text
print(f"学生ID:{student_id},姓名:{name},年龄:{age},专业:{major}")
# 处理完当前元素后清除,释放内存
elem.clear()Java语言下的XML解析实战
Java生态中也有完善的XML解析支持,标准库提供了DOM、SAX解析能力,第三方库如JDOM、Dom4j也广泛应用,下面给出标准库的解析示例。
1. DOM解析示例
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class DomParseXml {
public static void main(String[] args) {
try {
// 创建DOM解析器工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 解析XML文件,也可以是parse(new InputSource(new StringReader(xmlString)))
Document document = builder.parse("student.xml");
// 获取根节点
Element root = document.getDocumentElement();
// 获取所有student节点
NodeList studentNodes = root.getElementsByTagName("student");
for (int i = 0; i < studentNodes.getLength(); i++) {
Element student = (Element) studentNodes.item(i);
// 获取id属性
String studentId = student.getAttribute("id");
// 获取name子节点内容
String name = student.getElementsByTagName("name").item(0).getTextContent();
// 获取age子节点内容
String age = student.getElementsByTagName("age").item(0).getTextContent();
// 获取major子节点内容
String major = student.getElementsByTagName("major").item(0).getTextContent();
System.out.println("学生ID:" + studentId + ",姓名:" + name + ",年龄:" + age + ",专业:" + major);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}2. SAX解析示例
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class SaxParseXml {
public static void main(String[] args) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
// 自定义处理器
DefaultHandler handler = new DefaultHandler() {
private String currentTag = null;
private String studentId = null;
private String name = null;
private String age = null;
private String major = null;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
currentTag = qName;
if ("student".equals(qName)) {
studentId = attributes.getValue("id");
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("student".equals(qName)) {
System.out.println("学生ID:" + studentId + ",姓名:" + name + ",年龄:" + age + ",专业:" + major);
studentId = null;
name = null;
age = null;
major = null;
}
currentTag = null;
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String content = new String(ch, start, length).trim();
if (content.length() == 0) return;
if ("name".equals(currentTag)) {
name = content;
} else if ("age".equals(currentTag)) {
age = content;
} else if ("major".equals(currentTag)) {
major = content;
}
}
};
parser.parse("student.xml", handler);
} catch (Exception e) {
e.printStackTrace();
}
}
}XML解析的注意事项
在实际解析XML的过程中,有几个常见问题需要开发者注意:
- 特殊字符处理:XML内容中如果出现<、>、&等特殊字符,需要使用对应的实体引用,比如<代表<,&代表&,否则会导致解析报错。如果是文本内容中包含大量特殊字符,也可以把内容放在
<![CDATA[ ]]>块中,CDATA块中的内容不会被解析器解析。 - 大文件解析优化:如果XML文件体积超过内存限制,不要使用DOM解析,优先选择SAX或者StAX这类流式解析方式,避免内存溢出。同时解析完不需要的节点可以及时清除,释放内存。
- 编码问题:解析时要保证XML声明的编码和实际文件的编码一致,比如XML声明为encoding="UTF-8",那么文件保存时也要选择UTF-8编码,否则会出现乱码。
- 安全校验:如果解析外部不可信的XML文件,要避免XXE(XML外部实体注入)攻击,解析前可以关闭外部实体解析功能,比如在Java中通过factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)来禁用DOCTYPE声明。
总结
XML解析没有绝对最好的方法,只有最适合场景的选择。如果是小体积XML且需要修改内容,选DOM解析;如果是大体积XML仅需读取数据,选SAX或者StAX解析。Python和Java都提供了完善的原生解析支持,不需要额外依赖就能完成大部分解析需求,遇到复杂场景也可以考虑使用第三方库提升开发效率。掌握不同解析方式的原理和用法,能够帮助开发者更高效地应对各类XML相关的开发任务。
XML解析DOM解析SAX解析StAX解析Python解析XML修改时间:2026-05-24 22:20:54