Contents

如何使用 Java 讀寫 XML 文件

XML 文件可以用於多種用途,包括數據存儲。在 JSON 流行之前,XML 是表示、存儲和傳輸結構化數據的首選格式。

儘管 XML 在當代的流行度有所下降,但人們仍然可能會時不時地遇到 XML。因此,熟練使用這種格式至關重要。使用 Java 作為主要工具,探索利用文檔對像模型 (DOM) 應用程序編程接口 (API) 來讀取和寫入 XML 文檔的複雜性。

Java 處理 XML 的要求

Java 標準版 (SE) 包含用於 XML 處理的 Java API(通常稱為 JAXP),它是一個涵蓋 XML 處理各個方面的綜合框架。這個廣泛的曲目由幾個不可或缺的組成部分組成,包括:

文檔對像模型 (DOM) 包含一組有助於操作 XML 組件(包括元素、節點和屬性)的類。然而,考慮到其將整個 XML 文件加載到內存中進行處理的設計,它並不適合有效處理大尺寸文檔。

Simple API for XML (SAX) 是專門為處理 XML 文檔而設計的輕量級解析機制。與在處理過程中構建整個樹結構的文檔對像模型 (DOM) 不同,SAX 通過根據解析文件時遇到的 XML 內容觸發事件來進行操作。這種方法減少了內存消耗,並在處理大量數據方面提供了更大的靈活性。然而,與使用 DOM 相比,使用 SAX 可能有些挑戰性,因為它依賴於基於事件的編程範例。

StAX(即 XML 流 API)代表了 XML 處理領域的最新補充。這個強大的工具在流過濾、處理和修改方面擁有令人印象深刻的性能,無需將 XML 文檔批量加載到內存中即可實現其目標。與 SAX API 所青睞的事件驅動架構相比,StAX 採用拉式方法,這使得編碼變得更簡單、更用戶友好。

為了在 Java 應用程序中處理 XML 數據,有必要合併某些有助於實現此功能的包。這些包提供了用於解析、操作和生成 XML 文檔的各種方法和類。

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

準備示例 XML 文件

/bc/images/sample-xml-file-from-microsoft.jpeg

要了解示例代碼及其背後的概念,請使用此示例來自Microsoft 的XML 文件 。這是摘錄:

 <?xml version="1.0"?>
<catalog>
  <book id="bk101">
    <author>Gambardella, Matthew</author>
    <title>XML Developer's Guide</title>
    <genre>Computer</genre>
    <price>44.95</price>
    <publish_date>2000-10-01</publish_date>
    <description>An in-depth look at creating applications
      with XML.</description>
  </book>
  <book id="bk102">
    <author>Ralls, Kim</author>
...snipped... 

使用 DOM API 讀取 XML 文件

為了利用文檔對像模型(DOM)應用程序編程接口有效地處理 XML 文件,我們必須首先創建一個 DocumentBuilder 類的實例,這將有助於 XML 文檔的解析。

 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder(); 

現在,人們可以選擇將整個文檔存儲在內存中,從 XML 根元素開始,在本例中對應於“catalog”元素。

 // XML file to read
File file = "<path_to_file>";
Document document = builder.parse(file);
Element catalog = document.getDocumentElement();

事實上,通過利用這種方法,人們可以完全訪問整個 XML 文檔,從其主要節點(即“catalog”元素)開始。

使用 DOM API 提取信息

使用 XML 解析器獲得根元素後,您可以利用文檔對像模型 (DOM) API 來訪問其中有價值的信息。例如,檢索根元素的所有直接後代並迭代它們是一種實用的方法。但是,請注意,方法 getChildNodes() 返回所有類型的子節點,包括文本節點和註釋節點,這些節點與我們當前的任務無關。因此,在處理這些結果時,我們應該專門針對子元素。

 NodeList books = catalog.getChildNodes();

for (int i = 0, ii = 0, n = books.getLength() ; i < n ; i\+\+) {
  Node child = books.item(i);

  if ( child.getNodeType() != Node.ELEMENT_NODE )
    continue;

  Element book = (Element)child;
  // work with the book Element here
}

要使用 C# 在 XML 文檔中定位其父元素下的特定子元素,可以創建一個靜態方法,該方法迭代子節點集合以根據其名稱來識別所需元素。如果在此過程中發現該元素,則將其返回;否則,null 代表結果。

 static private Node findFirstNamedElement(Node parent,String tagName)
{
  NodeList children = parent.getChildNodes();

  for (int i = 0, in = children.getLength() ; i < in ; i\+\+) {
    Node child = children.item(i);

    if (child.getNodeType() != Node.ELEMENT_NODE)
      continue;

    if (child.getNodeName().equals(tagName))
      return child;
  }

  return null;
}

請注意,DOM API 將元素內包含的文本內容分類為類別 TEXT\_NODE 的單個節點。文本內容可能包含多個連續的文本節點,需要進行特定處理才能檢索給定元素的文本:

 static private String getCharacterData(Node parent)
{
  StringBuilder text = new StringBuilder();

  if ( parent == null )
    return text.toString();

  NodeList children = parent.getChildNodes();

  for (int k = 0, kn = children.getLength() ; k < kn ; k\+\+) {
    Node child = children.item(k);

    if (child.getNodeType() != Node.TEXT_NODE)
      break;

    text.append(child.getNodeValue());
  }

  return text.toString();
}

利用提供的實用函數,讓我們研究一個從表示圖書目錄的 XML 文檔中提取相關數據的示例。隨後的代碼顯示了有關目錄中包含的每個單獨出版物的全面詳細信息:

 NodeList books = catalog.getChildNodes();

for (int i = 0, ii = 0, n = books.getLength() ; i < n ; i\+\+) {
  Node child = books.item(i);

  if (child.getNodeType() != Node.ELEMENT_NODE)
    continue;

  Element book = (Element)child;
  ii\+\+;

  String id = book.getAttribute("id");
  String author = getCharacterData(findFirstNamedElement(child, "author"));
  String title = getCharacterData(findFirstNamedElement(child, "title"));
  String genre = getCharacterData(findFirstNamedElement(child, "genre"));
  String price = getCharacterData(findFirstNamedElement(child, "price"));
  String pubdate = getCharacterData(findFirstNamedElement(child, "pubdate"));
  String descr = getCharacterData(findFirstNamedElement(child, "description"));

  System.out.printf("%3d. book id = %s\n" \+
    " author: %s\n" \+
    " title: %s\n" \+
    " genre: %s\n" \+
    " price: %s\n" \+
    " pubdate: %s\n" \+
    " descr: %s\n",
    ii, id, author, title, genre, price, pubdate, descr);
}

這是代碼的逐步解釋:

該代碼仔細研究了巢穴或根組件的子組件,它是整個結構的基礎。

對於對應於特定書籍的每個單獨的子節點,程序驗證其底層數據結構是否具有作為 ELEMENT\_NODE 的特徵。如果不滿足此先決條件,則該過程將繼續進行後續迭代。

如果在 DOM 樹遍歷過程中遇到 ELEMENT\_NODE 類型的子節點,那麼 child 屬性將被強制轉換為 Element 接口的實例。

程序的後續執行涉及提取與指定書籍元素相關聯的多個屬性和字符數據,例如其唯一標識符(“id”)、作者姓名、標題、流派、價格、出版日期和描述信息。隨後,使用 System.out.printf() 方法將提取的信息打印到控制台以進行演示。

輸出如下所示:

/bc/images/parsing-xml-in-java-source-code-and-output.jpeg

使用 Transform API 編寫 XML 輸出

Java 提供 XML 轉換 API 作為操作 XML 數據的方法。該 API 與身份轉換結合使用以產生輸出。為了說明這一點,請考慮通過合併新的書籍元素來擴充之前的示例目錄。

與文學作品有關的信息,包括作者和標題,可以從諸如數據庫或屬性文件之類的外部資源獲得。提供的屬性文件用作此目的的模型。

 id=bk113
author=Jane Austen
title=Pride and Prejudice
genre=Romance
price=6.99
publish_date=2010-04-01
description="It is a truth universally acknowledged, that a single man in possession of a good fortune must be in want of a wife." So begins Pride and Prejudice, Jane Austen's witty comedy of manners-one of the most popular novels of all time-that features splendidly civilized sparring between the proud Mr. Darcy and the prejudiced Elizabeth Bennet as they play out their spirited courtship in a series of eighteenth-century drawing-room intrigues.

為了處理 XML 文檔,有必要利用前面概述的解析技術。這涉及分解文件的基於文本的結構並提取相關信息以供進一步分析或操作。

 File file = ...; // XML file to read
Document document = builder.parse(file);
Element catalog = document.getDocumentElement();

利用 Java 編程語言中提供的 Properties 類,可以有效地檢索和處理存儲在稱為“屬性”文件的單獨外部配置文件中的信息。此過程涉及最小的編碼複雜性,簡化了用戶定義的首選項或設置與整體應用程序邏輯的集成。

 String propsFile = "<path_to_file>";
Properties props = new Properties();

try (FileReader in = new FileReader(propsFile)) {
  props.load(in);
}

加載屬性文件後,可以從所述文件中提取所需的值以進行添加。

 String id = props.getProperty("id");
String author = props.getProperty("author");
String title = props.getProperty("title");
String genre = props.getProperty("genre");
String price = props.getProperty("price");
String publish_date = props.getProperty("publish_date");
String descr = props.getProperty("description");

現在,創建一個空的 book 元素。

 Element book = document.createElement("book");
book.setAttribute("id", id);

將本書的各個組成部分合併到文本語料庫中本身就是一項簡單的工作。為了促進這一過程,人們可以通過將必要的名稱組織成一個稱為“列表”的集合來編制必要的名稱目錄。通過執行該列表內的重複操作,各個條目可以有效地附加到更廣泛的敘述框架。

 List<String> elnames =Arrays.asList("author", "title", "genre", "price",
  "publish_date", "description");

for (String elname : elnames) {
  Element el = document.createElement(elname);
  Text text = document.createTextNode(props.getProperty(elname));
  el.appendChild(text);
  book.appendChild(el);
}

catalog.appendChild(book);

上述目錄組件當前擁有一個最近引入的附加書籍實體。剩下的唯一任務是製定包含所述更新的修訂版可擴展標記語言 (XML) 文檔。

為了使用轉換器生成 XML 文檔,必須首先獲得所述轉換器的實例。這可以通過在編程語言或開發環境中實現必要的代碼來完成。例如,在 Python 中,可以利用“transformers”庫及其相關函數來構造一個變壓器實例,以用於自然語言處理任務。

 TransformerFactory tfact = TransformerFactory.newInstance();
Transformer tform = tfact.newTransformer();
tform.setOutputProperty(OutputKeys.INDENT, "yes");
tform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3"); 

您可以使用 setOutputProperty() 方法來指定生成的輸出中所需的縮進級別。

最終階段涉及執行轉換過程。結果通過輸出流顯示,可以通過監視程序運行的控制台或終端來觀察。

 tform.transform(new DOMSource(document), new StreamResult(System.out));

要將程序的輸出保存到文件而不是將其打印到控制台,可以使用以下方法:

 tform.transform(new DOMSource(document), new StreamResult(new File("output.xml")));

為了使用Java編程語言執行可擴展標記語言(XML)文件的讀取和寫入,必須順序執行一系列過程操作。其中包括定義 XML 文檔對象、在該文檔中創建節點、將子元素附加到父節點、指定元素屬性、在文檔中的各個位置附加或插入新節點,以及最後在終止進程之前關閉所有打開的標記。

現在您知道如何使用 Java 讀寫 XML 文件

利用Java解析和操作可擴展標記語言(XML)是實際應用中經常遇到的必備技能。文檔對像模型 (DOM) 和轉換 API 對於此目的尤其有益。

對於尋求為基於 Web 的應用程序或網站創建客戶端腳本的開發人員來說,全面掌握文檔對像模型 (DOM) 是必不可少的。幸運的是,DOM 的架構在各種編程語言中都是標準化的,可以通過 Java 和 JavaScript 等語言編寫的代碼進行一致的操作。