Java XML处理
在企业应用开发中,XML
作为数据交换和配置管理的重要格式,几乎是每个 Java
开发者都要接触的技术。虽然现在 JSON
很流行,但在很多传统系统、企业级应用和标准协议中,XML
依然占据着不可替代的地位。
本文将带你深入了解 Java
中处理 XML
的各种方式,从基础的解析方法到实际项目中的最佳实践,帮你在面对 XML
处理需求时能够游刃有余。
# 一、XML 基础知识
# 1. 什么是 XML
XML
(Extensible Markup Language)是一种标记语言,主要用于数据的存储和传输。它的结构清晰、可读性强,特别适合在不同系统间进行数据交换。
# 2. 为什么 Java 项目中还在用 XML
虽然 JSON
更轻量,但 XML
在以下场景中仍有优势:
- 企业级应用:很多企业系统的接口标准就是基于
XML
- 配置文件:
Spring
、Maven
等框架广泛使用XML
配置 - 文档标准:
SOAP Web Service
、Office
文档格式等 - 数据验证:
XML Schema
提供了强大的数据验证能力
# 二、Java XML 处理技术概览
Java
处理 XML
主要有几种方式,各有特点:
- DOM:把整个文档加载到内存,适合小文件的增删改查
- SAX:事件驱动,内存占用少,适合大文件只读处理
- StAX:流式处理,在性能和易用性间取得平衡
- JAXB:对象绑定,让你像操作普通
Java
对象一样处理XML
接下来我们用一个具体的例子来演示这些技术。假设我们有这样一个书店的 XML
文件:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore xmlns="http://www.example.com/bookstore"
xmlns:author="http://www.example.com/author"
xmlns:publisher="http://www.example.com/publisher">
<book category="fiction">
<title>Harry Potter and the Philosopher's Stone</title>
<author:author>
<author:name>J.K. Rowling</author:name>
<author:birthplace>England</author:birthplace>
</author:author>
<publisher:publisher>
<publisher:name>Bloomsbury</publisher:name>
<publisher:location>London</publisher:location>
</publisher:publisher>
<price currency="USD">15.99</price>
</book>
<book category="non-fiction">
<title>The Lean Startup</title>
<author:author>
<author:name>Eric Ries</author:name>
<author:birthplace>United States</author:birthplace>
</author:author>
<publisher:publisher>
<publisher:name>Random House</publisher:name>
<publisher:location>New York</publisher:location>
</publisher:publisher>
<price currency="USD">19.99</price>
</book>
</bookstore>
# 1. DOM 解析:全量加载方式
DOM
解析是最直观的方式——就像把一本书全部加载到内存里,然后你可以随意翻阅、修改任意一页。它会把 XML
文档转换成一个树形结构,你可以轻松地增删改查任意节点。
优点:操作灵活,可随意修改文档任意部分
缺点:内存占用大,不适合处理大文件
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
try {
// 初始化 DOM 解析器
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true); // 支持命名空间
DocumentBuilder builder = factory.newDocumentBuilder();
// 解析 XML 文档
Document document = builder.parse("data.xml");
// 获取所有书籍节点
NodeList bookList = document.getElementsByTagName("book");
for (int i = 0; i < bookList.getLength(); i++) {
Element book = (Element) bookList.item(i);
// 读取书籍信息
String category = book.getAttribute("category");
String title = book.getElementsByTagName("title").item(0).getTextContent();
// 处理命名空间
NodeList authorNodes = book.getElementsByTagNameNS(
"http://www.example.com/author", "author");
String authorName = "";
if (authorNodes.getLength() > 0) {
Element author = (Element) authorNodes.item(0);
authorName = author.getElementsByTagNameNS(
"http://www.example.com/author", "name").item(0).getTextContent();
}
System.out.printf("书籍: %s, 作者: %s, 类别: %s%n",
title, authorName, category);
}
} catch (Exception e) {
e.printStackTrace();
}
# 2. SAX 解析:事件驱动方式
SAX
解析就像流水线上的工人——XML
数据一个个元素地流过,你只能在它们经过的时候处理,不能“回头看”。这种方式内存占用少,特别适合处理大型 XML
文件。
优点:内存占用小,适合大文件
缺点:只能顺序读取,不能修改文档
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
SAXParser parser = factory.newSAXParser();
// 自定义事件处理器
DefaultHandler handler = new DefaultHandler() {
private String currentElement = "";
private StringBuilder content = new StringBuilder();
private String currentBook = "";
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) {
currentElement = localName.isEmpty() ? qName : localName;
content.setLength(0); // 清空内容
if ("book".equals(currentElement)) {
String category = attributes.getValue("category");
System.out.println("开始处理书籍,类别: " + category);
}
}
@Override
public void characters(char[] ch, int start, int length) {
content.append(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) {
String elementName = localName.isEmpty() ? qName : localName;
String text = content.toString().trim();
if (!text.isEmpty()) {
switch (elementName) {
case "title":
System.out.println("书名: " + text);
break;
case "name":
if (uri.contains("author")) {
System.out.println("作者: " + text);
} else if (uri.contains("publisher")) {
System.out.println("出版社: " + text);
}
break;
case "price":
System.out.println("价格: " + text);
break;
}
}
if ("book".equals(elementName)) {
System.out.println("------");
}
}
};
parser.parse("data.xml", handler);
} catch (Exception e) {
e.printStackTrace();
}
# 3. StAX 解析:流式处理方式
StAX
是“最好的两个世界”——既有 SAX
的低内存占用,又有 DOM
那样的灵活性。与 SAX
被动接受事件不同,StAX
让你主动“拉取”数据,想读下一个元素的时候再读。
优点:低内存占用 + 灵活控制
缺点:只能向前读取,不能修改文档
import javax.xml.stream.*;
import java.io.FileInputStream;
try {
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader reader = factory.createXMLStreamReader(
new FileInputStream("data.xml"));
String currentElement = "";
while (reader.hasNext()) {
int event = reader.next();
switch (event) {
case XMLStreamConstants.START_ELEMENT:
currentElement = reader.getLocalName();
// 处理属性
if ("book".equals(currentElement)) {
String category = reader.getAttributeValue(null, "category");
System.out.println("书籍类别: " + category);
}
break;
case XMLStreamConstants.CHARACTERS:
String text = reader.getText().trim();
if (!text.isEmpty() && !currentElement.isEmpty()) {
switch (currentElement) {
case "title":
System.out.println("书名: " + text);
break;
case "name":
System.out.println("名称: " + text);
break;
case "price":
System.out.println("价格: " + text);
break;
}
}
break;
case XMLStreamConstants.END_ELEMENT:
if ("book".equals(reader.getLocalName())) {
System.out.println("------");
}
break;
}
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
# 4. XPath:像 SQL 一样查询 XML
XPath
就像给 XML
设计的 SQL
——你可以用一句话就准确地找到你要的数据。比如 /bookstore/book[1]/title
就能直接拿到第一本书的标题。
优点:表达式简洁,查询精准
缺点:需要先用 DOM
加载文档,内存占用较大
import javax.xml.parsers.*;
import org.w3c.dom.*;
import javax.xml.xpath.*;
import javax.xml.namespace.NamespaceContext;
import java.util.Iterator;
try {
// 先用 DOM 加载文档
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse("data.xml");
// 初始化 XPath
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xpath = xPathFactory.newXPath();
// 设置命名空间上下文
xpath.setNamespaceContext(new NamespaceContext() {
public String getNamespaceURI(String prefix) {
switch (prefix) {
case "bs": return "http://www.example.com/bookstore";
case "author": return "http://www.example.com/author";
case "pub": return "http://www.example.com/publisher";
default: return "";
}
}
public String getPrefix(String uri) { return null; }
public Iterator<String> getPrefixes(String uri) { return null; }
});
// 各种实用的 XPath 查询示例
// 1. 获取所有书的标题
NodeList titles = (NodeList) xpath.evaluate("//bs:book/bs:title",
document, XPathConstants.NODESET);
System.out.println("所有书的标题:");
for (int i = 0; i < titles.getLength(); i++) {
System.out.println("- " + titles.item(i).getTextContent());
}
// 2. 查找价格大于 16 的书
NodeList expensiveBooks = (NodeList) xpath.evaluate(
"//bs:book[bs:price > 16]/bs:title", document, XPathConstants.NODESET);
System.out.println("\n价格大于16的书:");
for (int i = 0; i < expensiveBooks.getLength(); i++) {
System.out.println("- " + expensiveBooks.item(i).getTextContent());
}
// 3. 获取第一本书的作者
String firstAuthor = (String) xpath.evaluate(
"//bs:book[1]/author:author/author:name", document, XPathConstants.STRING);
System.out.println("\n第一本书的作者: " + firstAuthor);
// 4. 统计书的数量
Double bookCount = (Double) xpath.evaluate("count(//bs:book)",
document, XPathConstants.NUMBER);
System.out.println("\n书的数量: " + bookCount.intValue());
} catch (Exception e) {
e.printStackTrace();
}
# 5. XSLT:让 XML 变身的魔法
XSLT
就像一个格式转换器,可以把 XML
数据“变身”成 HTML
、PDF
或者其他格式。你只需要写一个样式表,告诉它怎么转换,就能把枯燥的 XML
数据转成漂亮的网页。
适用场景:报表生成、数据展示、格式转换
import java.io.File;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
File xmlFile = new File("data.xml");
File xsltFile = new File("transform.xsl");
File outFile = new File("output.html");
StreamSource xmlSource = new StreamSource(xmlFile);
StreamSource xsltSource = new StreamSource(xsltFile);
StreamResult outputResult = new StreamResult(outFile);
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(xsltSource);
transformer.transform(xmlSource, outputResult);
System.out.println("Transformation completed.");
transform.xsl
文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:bookstore="http://www.example.com/bookstore"
xmlns:author="http://www.example.com/author"
xmlns:publisher="http://www.example.com/publisher"
exclude-result-prefixes="bookstore author publisher">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<body>
<h2>Books</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Author</th>
<th>Price</th>
</tr>
<xsl:apply-templates select="//bookstore:book"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="bookstore:book">
<tr>
<td><xsl:value-of select="bookstore:title"/></td>
<td><xsl:value-of select="author:author/author:name"/></td>
<td><xsl:value-of select="bookstore:price"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
输出的output.html
文件内容如下:
<html>
<body>
<h2>Books</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th><th>Author</th><th>Price</th>
</tr>
<tr>
<td>Harry Potter and the Philosopher's Stone</td><td>J.K. Rowling</td><td>15.99</td>
</tr>
<tr>
<td>The Lean Startup</td><td>Eric Ries</td><td>19.99</td>
</tr>
</table>
</body>
</html>
# 三、实战:常用 XML 操作技巧
现在我们从理论转向实践,看看在实际工作中怎么高效地处理 XML
。这些例子都是经过实际项目验证的,可以直接拿来用。
# 1. 动态创建 XML 文档
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
@Test
public void createXMLDocument() {
try {
// 创建文档工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 创建文档对象
Document document = builder.newDocument();
// 创建根元素
String rootElementName = "books";
org.w3c.dom.Element rootElement = document.createElement(rootElementName);
document.appendChild(rootElement);
// 创建子元素
String bookElementName = "book";
org.w3c.dom.Element bookElement = document.createElement(bookElementName);
rootElement.appendChild(bookElement);
// 添加子元素的属性
bookElement.setAttribute("id", "1");
// 添加子元素的文本内容
String title = "Java Programming";
org.w3c.dom.Element titleElement = document.createElement("title");
titleElement.appendChild(document.createTextNode(title));
bookElement.appendChild(titleElement);
// 将文档转换为字符串
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(document), new StreamResult(writer));
String xmlString = writer.toString();
// 断言输出的XML字符串是否符合预期
String expectedXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book></books>";
Assertions.assertEquals(xmlString, expectedXmlString);
} catch (Exception e) {
e.printStackTrace();
}
}
# 2. 从字符串解析 XML
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
@Test
public void readXMLDocument() {
try {
// XML文档字符串
String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book></books>";
// 创建文档工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 从XML字符串创建文档对象
Document document = builder.parse(new InputSource(new StringReader(xmlString)));
// 读取XML节点的值
String title = document.getElementsByTagName("title").item(0).getTextContent();
// 断言读取的节点值是否符合预期
Assertions.assertEquals(title, "Java Programming");
} catch (Exception e) {
e.printStackTrace();
}
}
# 3. SAX 解析实战示例
@Test
public void testSAXParsing() {
try {
// 创建 SAXParserFactory 对象
SAXParserFactory factory = SAXParserFactory.newInstance();
// 创建 SAXParser 对象
SAXParser saxParser = factory.newSAXParser();
// 解析 XML 文档
String xmlString = "<root><element1>Value 1</element1><element2>Value 2</element2></root>";
saxParser.parse(new InputSource(new StringReader(xmlString)), new DefaultHandler() {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
System.out.println("Start Element :" + qName + " " + localName + " " + uri);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.println("End Element :" + qName + " " + localName + " " + uri);
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String content = new String(ch, start, length).trim();
if (!content.isEmpty()) {
System.out.println("Content :" + content);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
# 4. 修改 XML 文档内容
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringReader;
import java.io.StringWriter;
@Test
public void modifyXMLDocument() {
try {
// XML文档字符串
String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book></books>";
// 创建文档工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 从XML字符串创建文档对象
Document document = builder.parse(new InputSource(new StringReader(xmlString)));
// 修改节点的值
Element titleElement = (Element) document.getElementsByTagName("title").item(0);
titleElement.setTextContent("Java Programming 2nd Edition");
// 将修改后的文档转换为字符串
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(document), new StreamResult(writer));
String modifiedXmlString = writer.toString();
// 断言修改后的XML字符串是否符合预期
String expectedModifiedXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming 2nd Edition</title></book></books>";
Assertions.assertEquals(modifiedXmlString, expectedModifiedXmlString);
} catch (Exception e) {
e.printStackTrace();
}
}
# 5. 删除指定节点
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringReader;
import java.io.StringWriter;
@Test
public void deleteXMLNode() {
try {
// XML文档字符串
String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book></books>";
// 创建文档工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 从XML字符串创建文档对象
Document document = builder.parse(new InputSource(new StringReader(xmlString)));
// 获取要删除的节点
Element bookElement = (Element) document.getElementsByTagName("book").item(0);
// 删除节点
Node parentNode = bookElement.getParentNode();
parentNode.removeChild(bookElement);
// 将修改后的文档转换为字符串
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(document), new StreamResult(writer));
String modifiedXmlString = writer.toString();
// 断言删除节点后的XML字符串是否符合预期
String expectedModifiedXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books/>";
Assertions.assertEquals(modifiedXmlString, expectedModifiedXmlString);
} catch (Exception e) {
e.printStackTrace();
}
}
# 6. 合并多个 XML 文档
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringReader;
import java.io.StringWriter;
@Test
public void mergeXMLDocuments() {
try {
// XML文档字符串1
String xmlString1 = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book></books>";
// XML文档字符串2
String xmlString2 = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"2\"><title>Python Programming</title></book></books>";
// 创建文档工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 从XML字符串创建文档对象1
Document document1 = builder.parse(new InputSource(new StringReader(xmlString1)));
// 从XML字符串创建文档对象2
Document document2 = builder.parse(new InputSource(new StringReader(xmlString2)));
// 获取根节点
Element rootElement1 = document1.getDocumentElement();
Element rootElement2 = document2.getDocumentElement();
// 将文档2中的所有子节点添加到文档1的根节点下
NodeList childNodes = rootElement2.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
Node importedNode = document1.importNode(node, true);
rootElement1.appendChild(importedNode);
}
// 将合并后的文档转换为字符串
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(document1), new StreamResult(writer));
String mergedXmlString = writer.toString();
// 断言合并后的XML字符串是否符合预期
String expectedMergedXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book><book id=\"2\"><title>Python Programming</title></book></books>";
Assertions.assertEquals(mergedXmlString, expectedMergedXmlString);
} catch (Exception e) {
e.printStackTrace();
}
}
# 7. 灵活查询 XML 数据
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
@Test
public void queryXMLData() {
try {
// XML文档字符串
String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book><book id=\"2\"><title>Python Programming</title></book></books>";
// 创建文档工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 从XML字符串创建文档对象
Document document = builder.parse(new InputSource(new StringReader(xmlString)));
// 查询XML数据
NodeList bookList = document.getElementsByTagName("book");
for (int i = 0; i < bookList.getLength(); i++) {
Node bookNode = bookList.item(i);
if (bookNode.getNodeType() == Node.ELEMENT_NODE) {
Element bookElement = (Element) bookNode;
String bookId = bookElement.getAttribute("id");
String title = bookElement.getElementsByTagName("title").item(0).getTextContent();
Assertions.assertEquals(bookId, i == 0 ? "1" : "2");
Assertions.assertEquals(title, i == 0 ? "Java Programming" : "Python Programming");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
# 四、主流 XML 处理库和工具
除了 Java
原生 API
,还有一些优秀的第三方库可以让 XML
处理更加轻松。根据我的经验,这里推荐几个在实际项目中经常用到的。
# 1. JAXP:Java 官方标准
JAXP
是 Java
内置的 XML
处理标准,就像是 XML
处理的“官方配套”。不管你用哪种 JDK
,都能直接使用,不需要额外的依赖。
优点:
- 官方支持,稳定可靠
- 支持
DOM
、SAX
、StAX
等多种解析方式 - 跨平台,兼容性好
缺点:
API
相对复杂,上手成本高- 功能相对基础,高级特性支持不够
# 2. JAXB:对象和 XML 的互转神器
JAXB (opens new window) 可能是最“懒人”的 XML
处理方式了——你只需要在 Java
类上加几个注解,就能让对象和 XML
之间自动转换。就像有了一个自动翻译器,不用你手动解析 XML
。
优点:
- 代码量少,开发效率高
- 类型安全,编译时可检查错误
- 支持复杂的对象关系映射
缺点:
- 需要额外的库依赖
- 对复杂
XML
结构支持不够灵活
# 2.1 示例
下面是一个使用 JAXB 进行 XML 到 Java 对象和 Java 对象到 XML 的示例:
首先,定义一个 Java 类 Book
,用于表示图书信息:
import jakarta.xml.bind.annotation.*;
@XmlRootElement(name = "book")
@XmlAccessorType(XmlAccessType.FIELD)
public class Book {
@XmlAttribute
private int id;
@XmlElement
private String title;
// Getter and Setter methods
// ...
}
然后,可以使用 JAXB 将 XML 转换为 Java 对象(XML 到 Java 对象):
import jakarta.xml.bind.*;
import java.io.File;
public class XMLToBeanExample {
public static void main(String[] args) {
try {
// 创建 JAXBContext 实例
JAXBContext context = JAXBContext.newInstance(Book.class);
// 创建 Unmarshaller 实例
Unmarshaller unmarshaller = context.createUnmarshaller();
// 从 XML 文件中读取 Book 对象
File xmlFile = new File("book.xml");
Book book = (Book) unmarshaller.unmarshal(xmlFile);
// 输出 Java 对象内容
System.out.println("Book ID: " + book.getId());
System.out.println("Title: " + book.getTitle());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
上述代码将从名为 "book.xml" 的 XML 文件中读取 Book 对象,并打印出其中的属性值。
接下来,可以使用 JAXB 将 Java 对象转换为 XML(Java 对象到 XML):
import jakarta.xml.bind.*;
import java.io.File;
public class BeanToXMLExample {
public static void main(String[] args) {
try {
// 创建 JAXBContext 实例
JAXBContext context = JAXBContext.newInstance(Book.class);
// 创建 Marshaller 实例
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// 创建 Book 对象
Book book = new Book();
book.setId(1);
book.setTitle("Java Programming");
// 将 Book 对象转换为 XML 文件
File xmlFile = new File("book.xml");
marshaller.marshal(book, xmlFile);
System.out.println("XML file created successfully.");
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
上述代码将创建一个 Book 对象,并将其转换为 XML 格式后写入名为 "book.xml" 的文件中。
这是一个简单的使用 JAXB 进行 XML 和 Java 对象之间转换的示例。通过定义适当的注解或配置文件,可以更复杂地映射 XML 结构和 Java 类的关系,以满足更具体的需求。
# 3. JDOM:最直观的 API
JDOM (opens new window) 就像是给 XML
处理重新设计了 API
,让你操作 XML
就像操作普通的 Java
对象一样简单。不用再为复杂的 DOM API
头疼了。
优点:
API
设计直观,学习成本低- 代码可读性强,维护方便
- 支持的功能全面
缺点:
- 性能相对一般
- 社区活跃度不高
参考: Getting Started (opens new window)
# 4. DOM4J:企业级的选择
DOM4J (opens new window) 可谓是 XML
处理界的“瓦尔特”——功能强大、性能优秀,在很多企业级项目中都有它的身影。特别是处理复杂、大型 XML
文档时,性能表现优秀。
优点:
- 性能优秀,特别适合大型
XML
- 支持强大的
XPath
查询 API
设计合理,功能全面- 在企业级项目中应用广泛
缺点:
- 需要额外的依赖
- 学习成本相对较高
参考: Quick Start (opens new window)
# 5. XStream:一行代码解决对象序列化
XStream (opens new window) 是最“懒”的对象序列化工具——一行代码就能把任意 Java
对象转成 XML
,再一行代码转回来。不需要任何注解,不需要配置文件。
优点:
- 使用极其简单,几乎零配置
- 对各种复杂对象支持好
- 生成的
XML
格式简洁
缺点:
- 生成的
XML
结构定制化程度低 - 安全性需要特别注意
参考: Tutorial (opens new window)
# 6. Jackson XML:万能的数据格式处理器
在Java JSON处理中我们介绍过 Jackson
的强大功能。现在它的 XML
模块 (opens new window)也能给你带来同样的便利性。
如果你的项目里已经在用 Jackson
处理 JSON
,那么增加 XML
支持就是加一个依赖的事情。API
风格完全一致,学习成本几乎为零。
优点:
- 与
Jackson JSON
处理风格一致 - 性能优秀,特别是大数据量处理
- 注解支持丰富,定制化程度高
缺点:
- 对一些复杂
XML
特性支持不全 - 学习成本相对较高
# 五、实际开发中的选择建议
读到这里,你可能会问:这么多种方式,在实际项目中到底该选哪一种?根据我多年的开发经验,给你几个建议:
# 1. 按场景选择
小文件,需要修改:直接用 DOM
,简单粗暴
大文件,只读取:首选 SAX
,内存占用小
需要精准查询:用 XPath
,一句话搞定
对象与 XML 互转:优先 JAXB
,实在不行考虑 XStream
企业级项目:DOM4J
是首选,成熟稳定
# 2. 性能调优小贴士
- 合理设置缓冲区:处理大文件时,调整
BufferedInputStream
的缓冲区大小 - 关闭不必要的验证:如果信任
XML
源,可以关闭DTD
验证提高性能 - 复用解析器实例:
DocumentBuilder
等对象创建成本较高,能复用就复用 - 及时释放资源:特别是使用
StAX
时,记得关闭reader
# 3. 安全性注意事项
- XXE 攻击防护:在生产环境中处理外部
XML
时,一定要禁用外部实体解析 - 内存溢出防护:对输入
XML
的大小做限制,防止恶意XML
攻击 - 输入校验:对于用户输入的
XML
,做好格式和内容校验
总的来说,XML
处理虽然看起来复杂,但掌握了这些技术后,你就能在面对各种 XML
处理需求时得心应手。记住,没有最好的技术,只有最适合的选择。
祝你变得更强!