Bài viết này trình bày bốn phương pháp phổ biến nhất để xử lý XML trong Java: DOM, SAX, StAX và JDOM. Mỗi phương pháp có ưu nhược điểm riêng phù hợp với các tình huống khác nhau. Dưới đây là phân tích chi tiết kèm theo ví dụ minh họa.
1. Tổng quan về các kỹ thuật phân tích XML
XML được sử dụng rộng rãi trong các ứng dụng Java để lưu trữ cấu hình, trao đổi dữ liệu giữa hệ thống hoặc trong giao thức SOAP. Java cung cấp nhiều cách tiếp cận khác nhau để xử lý XML:
- DOM: Tải toàn bộ tài liệu vào bộ nhớ dưới dạng cây, thích hợp cho tệp nhỏ
- SAX: Xử lý theo sự kiện, tiết kiệm bộ nhớ, phù hợp với tệp lớn
- StAX: Giao diện API luồng hai chiều, linh hoạt hơn SAX
- JDOM: API thân thiện với Java, đơn giản hóa quy trình phát triển
2. Phương pháp DOM: Phân tích cây XML
DOM xây dựng cây DOM từ tệp XML và lưu trữ toàn bộ trong bộ nhớ. Cách tiếp cận này phù hợp khi cần truy cập ngẫu nhiên hoặc sửa đổi dữ liệu.
Ví dụ minh họa:
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;
public class XmlTreeAnalyzer {
public static void analyzeNodeStructure(Node node, int depth) {
String indent = " ".repeat(depth);
System.out.print(indent + "Loại: ");
switch (node.getNodeType()) {
case Node.DOCUMENT_NODE:
System.out.println("Document");
break;
case Node.ELEMENT_NODE:
System.out.println("Element [" + node.getNodeName() + "]");
if (node.hasAttributes()) {
NamedNodeMap attributes = node.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node attr = attributes.item(i);
System.out.println(indent + " Thuộc tính: " + attr.getNodeName() +
" = \"" + attr.getNodeValue() + "\"");
}
}
break;
case Node.TEXT_NODE:
String textContent = node.getTextContent().trim();
if (!textContent.isEmpty()) {
System.out.println("Text = \"" + textContent + "\"");
}
return;
case Node.COMMENT_NODE:
System.out.println("Comment: " + node.getNodeValue());
return;
default:
System.out.println(node.getNodeName() + " (" + node.getNodeType() + ")");
}
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
analyzeNodeStructure(child, depth + 1);
}
}
}
3. Phương pháp SAX: Xử lý sự kiện
SAX là phương pháp xử lý theo sự kiện, không lưu toàn bộ tài liệu vào bộ nhớ. Nó phù hợp cho các tệp XML lớn.
Ví dụ minh họa:
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
public class XmlEventProcessor {
public static void main(String[] args) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
SAXParser parser = factory.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.setContentHandler(new BookDataHandler());
reader.parse(new File("books.xml"));
}
}
class BookDataHandler extends DefaultHandler {
private String currentBookId;
private StringBuilder currentContent = new StringBuilder();
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if ("book".equals(localName)) {
currentBookId = attributes.getValue("id");
currentContent = new StringBuilder();
}
}
@Override
public void characters(char[] ch, int start, int length) {
currentContent.append(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) {
if ("title".equals(localName) || "author".equals(localName)) {
System.out.println("[" + currentBookId + "] " + localName + ": " + currentContent.toString().trim());
}
currentContent = new StringBuilder();
}
}
4. Phương pháp StAX: API luồng hai chiều
StAX kết hợp ưu điểm của DOM và SAX, cho phép người lập trình kiểm soát luồng xử lý.
Ví dụ minh họa:
import javax.xml.stream.*;
import java.io.File;
public class StaxXmlProcessor {
public static void processOrderData(String filePath) throws Exception {
XMLInputFactory factory = XMLInputFactory.newInstance();
try (FileInputStream fis = new FileInputStream(filePath);
XMLEventReader reader = factory.createXMLEventReader(fis)) {
String currentOrderId = null;
String customerName = null;
double orderTotal = 0.0;
while (reader.hasNext()) {
XMLEvent event = reader.nextEvent();
if (event.isStartElement()) {
StartElement startEl = event.asStartElement();
String localName = startEl.getName().getLocalPart();
if ("order".equals(localName)) {
currentOrderId = startEl.getAttributeByName(new QName("id")).getValue();
} else if ("customer".equals(localName)) {
customerName = startEl.getAttributeByName(new QName("name")).getValue();
} else if ("total".equals(localName)) {
orderTotal = Double.parseDouble(startEl.getAttributeByName(new QName("amount")).getValue());
}
}
if (event.isEndElement() && "order".equals(event.asEndElement().getName().getLocalPart())) {
System.out.println("Đơn hàng " + currentOrderId + ": " + customerName + ", Tổng: " + orderTotal);
currentOrderId = null;
customerName = null;
orderTotal = 0.0;
}
}
}
}
}
5. So sánh hiệu năng các phương pháp
| Kích thước tệp | DOM (MB) | SAX (MB) | StAX (MB) | JDOM (MB) |
|---|---|---|---|---|
| 10 KB | 2.1 | 0.8 | 0.9 | 2.3 |
| 100 MB | Out of Memory | 3.8 | 3.9 | Out of Memory |
DOM và JDOM phù hợp với tệp nhỏ, trong khi SAX và StAX hiệu quả với tệp lớn.