在XML数据处理中,文件里的元素往往既包含嵌套的文本或子节点,也带有描述自身特征的属性。读取这些属性是配置解析、接口报文处理的基础操作。属性并不作为独立的子元素存在,而是直接挂在元素节点上,因此不能用获取文本的方式去取,必须使用解析器提供的属性访问接口。

一、DOM方式读取XML属性
DOM(Document Object Model)将整个XML文档加载为内存中的树结构,适合文件不大、需要随机访问的场景。在Java标准库中,解析后的元素类型为org.w3c.dom.Element,它继承自Node并扩展了属性相关方法。最常用的是getAttribute(String name),当属性不存在时返回空字符串,而不会抛异常,这点比getAttributeNode更省心。
下面示例展示如何用Java DOM读取book元素的id与category属性。先创建工厂并解析输入流,再按标签名取到节点列表,对每个元素直接调用getAttribute。注意如果XML带有命名空间,属性名需对应本地名称,否则可能取不到值。
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;
public class ReadXmlAttr {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 假设xml内容包含 <book id="b1" category="tech">Java</book>
Document doc = builder.parse(new ByteArrayInputStream(
"<root><book id='b1' category='tech'>Java</book></root>".getBytes()));
NodeList books = doc.getElementsByTagName("book");
for (int i = 0; i < books.getLength(); i++) {
Element book = (Element) books.item(i);
String id = book.getAttribute("id");
String category = book.getAttribute("category");
System.out.println("id=" + id + ", category=" + category);
}
}
}
DOM方式的优势是结构直观、可反复遍历,改完还能写回文件。缺点是文档全量载入,遇到几十MB以上的XML会占用过多内存。若仅需提取少量属性且文件庞大,应考虑流式解析。
二、SAX方式读取XML属性
SAX(Simple API for XML)基于事件驱动,边读边解析,内存占用极低。在Java中需继承DefaultHandler,重写startElement方法。该方法签名中的Attributes参数专门承载当前元素的属性集合,通过getValue(String qName)或按索引取即可。
以下代码演示SAX提取同样的两个属性。注意字符数据需在characters中拼接,属性则仅在开始标签触发时可用,错过回调就取不到了。这种模型要求开发者对解析顺序有清晰认知。
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;
public class SaxAttr extends DefaultHandler {
public void startElement(String uri, String localName, String qName, Attributes attrs) {
if ("book".equals(qName)) {
String id = attrs.getValue("id");
String category = attrs.getValue("category");
System.out.println("id=" + id + ", category=" + category);
}
}
public static void main(String[] args) throws Exception {
SAXParserFactory f = SAXParserFactory.newInstance();
SAXParser p = f.newSAXParser();
String xml = "<root><book id='b1' category='tech'/></root>";
p.parse(new java.io.ByteArrayInputStream(xml.getBytes()), new SaxAttr());
}
}
SAX适合日志型、大报文等顺序处理任务,但代码不如DOM易读,且无法回退。实际项目中常封装一层,将关心的属性映射到业务对象,降低直接使用原生API的心智负担。
三、Python中读取XML属性
Python标准库的xml.etree.ElementTree把元素当作带字典接口的对象,属性存储在元素的attrib中,也可用下标语法直接取。相比Java更轻量,脚本化处理配置文件非常方便。
示例里先用fromstring构造树,再迭代找到book节点,打印其attrib字典与指定键。若属性可能缺失,用element.get('key', 'default')可避免KeyError,比直接下标更健壮。
import xml.etree.ElementTree as ET
xml_text = "<root><book id='b1' category='tech'>Python</book></root>"
root = ET.fromstring(xml_text)
for book in root.findall('book'):
print(book.attrib)
print("id=", book.get('id'), "category=", book.get('category', 'none'))
Python方案在运维工具、小型服务中很常见。若XML来自不可信外部,应改用defusedxml库以防实体注入攻击,这是安全层面的必要考量。
四、常见错误与排查
初学者常把属性当子标签读取,例如用getElementsByTagName("id")去找属性,结果永远为空。必须明确:写在开始标签括号内的才是属性,写成<id>b1</id>的才是子元素。混淆二者会导致解析逻辑完全失效。
另一个坑是命名空间。当XML声明了xmlns前缀,属性若带前缀则需用带命名空间的方法获取。建议先用工具打印元素所有属性名,确认实际字符串后再写代码,可大幅减少调试时间。
| 解析方式 | 内存占用 | 适用场景 |
|---|---|---|
| DOM | 高 | 小文件、需修改回写 |
| SAX | 低 | 大文件、顺序读取 |
| ElementTree | 中 | Python脚本、配置处理 |
掌握上述方法与差异,就能在各类技术栈中稳妥地读取XML文件属性,支撑配置加载、数据交换等核心功能。