首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 遍歷讀取 XML 文件內容完整指南

     編輯:Java 相關知識

核心解析方式對比

Java 提供四種主流 XML 解析 API,適用場景不同:

API類型特點適用場景
DOM樹模型一次性載入整個文檔到內存,支持隨機訪問、修改小文件(<10MB)、需頻繁修改、XPath 查詢
SAX事件流順序讀取、不佔用文檔內存、只讀、回調式大文件、單次遍歷、內存受限環境
StAX拉取流游標/迭代器模式、可控制解析進程、只讀/寫入大文件、需精確控制解析邏輯、性能敏感
JAXB綁定XML ↔ Java 對象自動映射、註解驅動配置文件、數據交換、對象序列化

DOM 解析:完整樹遍歷

public void parseWithDom(String filePath) throws Exception {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true);
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document doc = builder.parse(new File(filePath));
    doc.getDocumentElement().normalize();

    // 1. 獲取根元素
    Element root = doc.getDocumentElement();
    System.out.println("根元素: " + root.getTagName());

    // 2. 遞歸遍歷所有節點
    traverseNode(root, 0);
}

private void traverseNode(Node node, int depth) {
    String indent = "  ".repeat(depth);
    if (node.getNodeType() == Node.ELEMENT_NODE) {
        Element elem = (Element) node;
        System.out.println(indent + "<" + elem.getTagName() + ">");
        // 屬性
        NamedNodeMap attrs = elem.getAttributes();
        for (int i = 0; i < attrs.getLength(); i++) {
            Node attr = attrs.item(i);
            System.out.println(indent + "  @" + attr.getNodeName() + "=" + attr.getNodeValue());
        }
    } else if (node.getNodeType() == Node.TEXT_NODE && node.getTextContent().trim().length() > 0) {
        System.out.println(indent + "#text: " + node.getTextContent().trim());
    }

    // 遞歸子節點
    NodeList children = node.getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
        traverseNode(children.item(i), depth + 1);
    }
}

SAX 解析:事件驅動流式讀取

public void parseWithSax(String filePath) throws Exception {
    SAXParserFactory factory = SAXParserFactory.newInstance();
    factory.setNamespaceAware(true);
    SAXParser parser = factory.newSAXParser();

    DefaultHandler handler = new DefaultHandler() {
        private int depth = 0;
        private StringBuilder textBuffer = new StringBuilder();

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attrs) {
            System.out.println("  ".repeat(depth) + "<" + qName + ">");
            for (int i = 0; i < attrs.getLength(); i++) {
                System.out.println("  ".repeat(depth + 1) + "@" + attrs.getQName(i) + "=" + attrs.getValue(i));
            }
            depth++;
        }

        @Override
        public void characters(char[] ch, int start, int length) {
            textBuffer.append(ch, start, length);
        }

        @Override
        public void endElement(String uri, String localName, String qName) {
            depth--;
            String text = textBuffer.toString().trim();
            if (!text.isEmpty()) {
                System.out.println("  ".repeat(depth) + "#text: " + text);
            }
            textBuffer.setLength(0);
            System.out.println("  ".repeat(depth) + "</" + qName + ">");
        }
    };

    parser.parse(new File(filePath), handler);
}

StAX 解析:游標式拉取控制

public void parseWithStax(String filePath) throws Exception {
    XMLInputFactory factory = XMLInputFactory.newInstance();
    XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream(filePath));

    while (reader.hasNext()) {
        int event = reader.next();
        switch (event) {
            case XMLStreamConstants.START_ELEMENT:
                System.out.println("<" + reader.getLocalName() + ">");
                for (int i = 0; i < reader.getAttributeCount(); i++) {
                    System.out.println("  @" + reader.getAttributeLocalName(i) + "=" + reader.getAttributeValue(i));
                }
                break;
            case XMLStreamConstants.CHARACTERS:
                String text = reader.getText().trim();
                if (!text.isEmpty()) System.out.println("#text: " + text);
                break;
            case XMLStreamConstants.END_ELEMENT:
                System.out.println("</" + reader.getLocalName() + ">");
                break;
        }
    }
    reader.close();
}

JAXB 綁定:XML 轉 Java 對象

// 實體類
@XmlRootElement(name = "book")
@XmlAccessorType(XmlAccessType.FIELD)
public class Book {
    @XmlAttribute private String id;
    @XmlElement private String title;
    @XmlElement private String author;
    @XmlElement private double price;
    // getter/setter/toString
}

// 解析
public List<Book> parseWithJaxb(String filePath) throws JAXBException {
    JAXBContext context = JAXBContext.newInstance(Book.class);
    Unmarshaller unmarshaller = context.createUnmarshaller();
    // 假設 XML 根元素為 <books> 包含多個 <book>
    // 需要包裝類或使用 JAXBElement
    return ... 
}

XPath 查詢:精確定位節點

public void queryWithXPath(String filePath) throws Exception {
    Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(filePath);
    XPathFactory xpf = XPathFactory.newInstance();
    XPath xpath = xpf.newXPath();

    // 查詢所有書名
    NodeList titles = (NodeList) xpath.evaluate("//book/title", doc, XPathConstants.NODESET);
    for (int i = 0; i < titles.getLength(); i++) {
        System.out.println(titles.item(i).getTextContent());
    }

    // 查詢價格 > 50 的書籍
    NodeList expensive = (NodeList) xpath.evaluate("//book[price > 50]", doc, XPathConstants.NODESET);
    // ...
}

大文件處理最佳實踐

  1. 優先選擇 SAX 或 StAX:常量內存佔用,避免 OOM。
  2. StAX 更靈活:可隨時停止、跳過、分支處理,適合複雜邏輯。
  3. 分塊處理:結合 XMLStreamReader 逐元素讀取,處理完即釋放。
  4. 禁用 DTD/外部實體:防止 XXE 攻擊,factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)。
  5. 命名空間感知:生產環境必須 setNamespaceAware(true)。

常見異常與排查

異常原因解決
SAXParseException: Premature end of file文件為空、截斷、或 InputStream 被重複讀取檢查文件完整性、確保流只讀一次
MalformedByteSequenceException編碼不匹配(如 UTF-8 文件用 GBK 讀)指定正確編碼、或檢測 BOM 自動識別
OutOfMemoryErrorDOM 解析超大文件改用 SAX/StAX

總結

根據文件大小、內存限制、處理邏輯選擇解析器:小文件隨機訪問用 DOM,大文件順序遍歷用 SAX/StAX,對象映射用 JAXB,精確查詢用 XPath。生產代碼務必開啟安全特性防 XXE,妥善處理編碼與異常。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved