Java XML 處理

Java 提供多種處理 XML 的 API,包括 DOM、SAX、StAX 和 JAXB。

DOM 解析

將整個 XML 載入記憶體,適合小型 XML。

import javax.xml.parsers.*;
import org.w3c.dom.*;

String xml = """
    <?xml version="1.0"?>
    <users>
        <user id="1">
            <name>Alice</name>
            <age>25</age>
        </user>
        <user id="2">
            <name>Bob</name>
            <age>30</age>
        </user>
    </users>
    """;

// 解析
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xml)));

// 讀取元素
NodeList users = doc.getElementsByTagName("user");
for (int i = 0; i < users.getLength(); i++) {
    Element user = (Element) users.item(i);
    String id = user.getAttribute("id");
    String name = user.getElementsByTagName("name").item(0).getTextContent();
    String age = user.getElementsByTagName("age").item(0).getTextContent();
    System.out.println(id + ": " + name + ", " + age);
}

DOM 建立 XML

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();

// 建立元素
Element root = doc.createElement("users");
doc.appendChild(root);

Element user = doc.createElement("user");
user.setAttribute("id", "1");
root.appendChild(user);

Element name = doc.createElement("name");
name.setTextContent("Alice");
user.appendChild(name);

// 輸出
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(new DOMSource(doc), new StreamResult(System.out));

SAX 解析

事件驅動,適合大型 XML,記憶體效率高。

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;

class UserHandler extends DefaultHandler {
    StringBuilder content = new StringBuilder();
    String currentElement;
    
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attrs) {
        currentElement = qName;
        if ("user".equals(qName)) {
            System.out.println("User ID: " + attrs.getValue("id"));
        }
    }
    
    @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) {
        if ("name".equals(qName)) {
            System.out.println("Name: " + content.toString().trim());
        }
        content.setLength(0);
    }
}

// 使用
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse(new InputSource(new StringReader(xml)), new UserHandler());

StAX 解析

串流 API,介於 DOM 和 SAX 之間。

import javax.xml.stream.*;

XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader reader = factory.createXMLStreamReader(new StringReader(xml));

while (reader.hasNext()) {
    int event = reader.next();
    
    switch (event) {
        case XMLStreamConstants.START_ELEMENT:
            System.out.println("開始: " + reader.getLocalName());
            break;
        case XMLStreamConstants.CHARACTERS:
            String text = reader.getText().trim();
            if (!text.isEmpty()) {
                System.out.println("內容: " + text);
            }
            break;
        case XMLStreamConstants.END_ELEMENT:
            System.out.println("結束: " + reader.getLocalName());
            break;
    }
}
reader.close();

JAXB(Java 11+ 需額外引入)

將 XML 和 Java 物件互相轉換。

import jakarta.xml.bind.*;
import jakarta.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class User {
    @XmlAttribute
    private int id;
    private String name;
    private int age;
    // getters, setters
}

// 物件轉 XML(序列化)
User user = new User(1, "Alice", 25);
JAXBContext context = JAXBContext.newInstance(User.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(user, System.out);

// XML 轉物件(反序列化)
Unmarshaller unmarshaller = context.createUnmarshaller();
User parsed = (User) unmarshaller.unmarshal(new StringReader(xmlString));

XPath 查詢

import javax.xml.xpath.*;

XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();

Document doc = ...; // DOM Document

// 查詢單一值
String name = xpath.evaluate("/users/user[@id='1']/name", doc);

// 查詢節點集
XPathExpression expr = xpath.compile("//user");
NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);

方法比較

方法記憶體速度隨機存取適用場景
DOM較慢支援小型 XML、需要修改
SAX不支援大型 XML、只讀
StAX有限大型 XML、串流
JAXB支援物件映射

重點整理

  • DOM:載入整個 XML,適合小型檔案和需要修改的場景
  • SAX:事件驅動,記憶體效率高,適合大型檔案
  • StAX:拉取式解析,比 SAX 更直觀
  • JAXB:物件與 XML 互轉,需要 Java 11+ 額外引入
  • XPath:強大的查詢語言