使用 Java 解析XML檔案

kenaiou發表於2022-04-07

當您編寫應用程式時,您通常希望使用者自定義他們與應用程式的互動方式以及應用程式與系統的互動方式。這種方法通常稱為“首選項”或“設定”,它們儲存在“首選項檔案”或“配置檔案”中,有時簡稱為“配置配置”。配置檔案可以有多種格式,包括INI、JSON、YAML和XML。每種程式語言都以不同的方式解析這些格式。本文主要討論在用Java程式語言編寫軟體時,如何實現持久化配置。


選擇一種格式。


編寫配置檔案相當複雜。我嘗試過將配置項儲存在用逗號分隔的文字檔案中,也嘗試過將配置項儲存在非常詳細的YAML和XML中。對於配置檔案來說,最重要的是一致性和規律性。它們使您能夠簡單快速地編寫程式碼並從配置檔案中解析資料。同時,當使用者決定進行更改時,可以方便地儲存和更新配置。


目前有幾種流行的配置檔案格式。對於大多數常見的配置檔案格式,Java都有相應的庫。在本文中,我將使用XML格式。對於某些專案,您可能會選擇使用XML,因為它的突出特點是可以為所包含的資料提供大量的相關後設資料,而對於其他專案,您可能會因為其冗長性而不選擇XML。在Java中使用XML非常容易,因為預設情況下它包含許多健壯的XML庫。

<p>


XML基礎


</p>


<p>


對XML的討論是一個大話題。我有一本關於XML的書,有700多頁。幸運的是,使用XML不需要了解太多它的特性。就像HTML一樣,XML是一種帶有開始和結束標籤的分層標記語言,每個標籤(tag)可以包含零個或多個資料。下面是一個簡單的XML示例片段:


企鵝 在這個自我描述的示例中,XML解析器使用了以下概念:


文件:標籤標記文件的開始,標籤標記文件的結束。


節點:標籤代表一個節點。


元素:企鵝,從頭到尾,代表一種元素。


內容:在元素中,字串企鵝就是內容。


信不信由你,只要你知道了上面的概念,你就可以開始編寫和解析XML檔案了。

建立一個示例配置檔案。


要學習如何解析XML檔案,只需要一個最小的樣本檔案就足夠了。現在假設有一個配置檔案,它儲存了圖形介面視窗的屬性:


黑暗0Tango


建立一個名為~/的目錄。config/DemoXMLParser:

$ mkdir  ~/。config/DemoXMLParser

使用 Java 解析 XML

如果你是 Java 的初學者,你可以先閱讀我寫的  。一旦你對 Java 比較熟悉了,開啟你最喜愛的整合開發工具(IDE),建立一個新工程。我會把我的新工程命名為  myConfigParser

剛開始先不要太關注依賴匯入和異常捕獲這些,你可以先嚐試用  javax 和  java.io 包裡的標準 Java 擴充套件來例項化一個解析器。如果你使用了 IDE,它會提示你匯入合適的依賴。如果沒有,你也可以在文章稍後的部分找到完整的程式碼,裡面就有完整的依賴列表。

Path configPath = Paths.get(System.getProperty("user.home"), ".config", "DemoXMLParser");File configFile = new File(configPath.toString(), "myconfig.xml");DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = null;builder = factory.newDocumentBuilder();Document doc = null;doc = builder.parse(configFile);doc.getDocumentElement().normalize();

這段示例程式碼使用了  java.nio.Paths 類來找到使用者的主目錄,然後在拼接上預設配置檔案的路徑。接著,它用  java.io.File 類來把配置檔案定義為一個  File 物件。

緊接著,它使用了  javax.xml.parsers.DocumentBuilder 和  javax.xml.parsers.DocumentBuilderFactory 這兩個類來建立一個內部的文件構造器,這樣 Java 程式就可以匯入並解析 XML 資料了。

最後,Java 建立一個叫  doc 的文件物件,並且把  configFile 檔案載入到這個物件裡。透過使用  org.w3c.dom 包,它讀取並規範化了 XML 資料。

基本上就是這樣啦。理論上來講,你已經完成了資料解析的工作。可是,如果你不能夠訪問資料的話,資料解析也沒有多少用處嘛。所以,就讓我們再來寫一些查詢,從你的配置中讀取重要的屬性值吧。

使用 Java 訪問 XML 的值

從你已經讀取的 XML 文件中獲取資料,其實就是要先找到一個特定的節點,然後遍歷它包含的所有元素。通常我們會使用多個迴圈語句來遍歷節點中的元素,但是為了保持程式碼可讀性,我會盡可能少地使用迴圈語句:

NodeList nodes = doc.getElementsByTagName("window");for (int i = 0; i < nodes.getLength(); i++) { Node mynode = nodes.item(i); System.out.println("Property = " + mynode.getNodeName());        if (mynode.getNodeType() == Node.ELEMENT_NODE) {   Element myelement = (Element) mynode;                System.out.println("Theme = " + myelement.getElementsByTagName("theme").item(0).getTextContent());   System.out.println("Fullscreen = " + myelement.getElementsByTagName("fullscreen").item(0).getTextContent());   System.out.println("Icon set = " + myelement.getElementsByTagName("icons").item(0).getTextContent()); }}

這段示例程式碼使用了  org.w3c.dom.NodeList 類,建立了一個名為  nodes 的  NodeList 物件。這個物件包含了所有名字匹配字串  window 的子節點,實際上這樣的節點只有一個,因為本文的示例配置檔案中只配置了一個。

緊接著,它使用了一個  for 迴圈來遍歷  nodes 列表。具體過程是:根據節點出現的順序逐個取出,然後交給一個  if-then 子句處理。這個  if-then 子句建立了一個名為  myelement 的  Element 物件,其中包含了當前節點下的所有元素。你可以使用例如  getChildNodes 和  getElementById 方法來查詢這些元素,專案中還  記錄了 其他查詢方法。

在這個示例中,每個元素就是配置的鍵。而配置的值儲存在元素的內容中,你可以使用  .getTextContent 方法來提取出配置的值。

在你的 IDE 中執行程式碼(或者執行編譯後的二進位制檔案):

$ java ./DemoXMLParser.javaProperty = windowTheme = DarkFullscreen = 0Icon set = Tango

下面是完整的程式碼示例:

package myConfigParser;import java.io.File;import java.io.IOException;import java.nio.file.Path;import java.nio.file.Paths;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.ParserConfigurationException;import org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.NamedNodeMap;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import org.xml.sax.SAXException;public class ConfigParser {        public static void main(String[] args) {                Path configPath = Paths.get(System.getProperty("user.home"), ".config", "DemoXMLParser");                File configFile = new File(configPath.toString(), "myconfig.xml");                DocumentBuilderFactory factory =                DocumentBuilderFactory.newInstance();                DocumentBuilder builder = null;                               try {                        builder = factory.newDocumentBuilder();                } catch (ParserConfigurationException e) {                        e.printStackTrace();                }                       Document doc = null;                       try {                        doc = builder.parse(configFile);                } catch (SAXException e) {                        e.printStackTrace();                } catch (IOException e) {                        e.printStackTrace();                }        doc.getDocumentElement().normalize();               NodeList nodes = doc.getElementsByTagName("window");        for (int i = 0; i < nodes.getLength(); i++) {           Node mynode = nodes.item(i);           System.out.println("Property = " + mynode.getNodeName());                      if (mynode.getNodeType() == Node.ELEMENT_NODE) {               Element myelement = (Element) mynode;               System.out.println("Theme = " + myelement.getElementsByTagName("theme").item(0).getTextContent());               System.out.println("Fullscreen = " + myelement.getElementsByTagName("fullscreen").item(0).getTextContent());               System.out.println("Icon set = " + myelement.getElementsByTagName("icons").item(0).getTextContent());           } // close if        } // close for    } // close method} //close class

使用 Java 更新 XML

使用者時不時地會改變某個偏好項,這時候  org.w3c.dom 庫就可以幫助你更新某個 XML 元素的內容。你只需要選擇這個 XML 元素,就像你讀取它時那樣。不過,此時你不再使用  .getTextContent 方法,而是使用  .setTextContent 方法。

updatePref = myelement.getElementsByTagName("fullscreen").item(0);updatePref.setTextContent("1");System.out.println("Updated fullscreen to " + myelement.getElementsByTagName("fullscreen").item(0).getTextContent());

這麼做會改變應用程式記憶體中的 XML 文件,但是還沒有把資料寫回到磁碟上。配合使用  javax 和  w3c 庫,你就可以把讀取到的 XML 內容寫回到配置檔案中。

TransformerFactory transformerFactory = TransformerFactory.newInstance();Transformer xtransform;xtransform = transformerFactory.newTransformer();DOMSource mydom = new DOMSource(doc);StreamResult streamResult = new StreamResult(configFile);xtransform.transform(mydom, streamResult);

這麼做會沒有警告地寫入轉換後的資料,並覆蓋掉之前的配置。

下面是完整的程式碼,包括更新 XML 的操作:

package myConfigParser;import java.io.File;import java.io.IOException;import java.nio.file.Path;import java.nio.file.Paths;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.ParserConfigurationException;import javax.xml.transform.Transformer;import javax.xml.transform.TransformerException;import javax.xml.transform.TransformerFactory;import javax.xml.transform.dom.DOMSource;import javax.xml.transform.stream.StreamResult;import org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import org.xml.sax.SAXException;public class ConfigParser {        public static void main(String[] args) {                Path configPath = Paths.get(System.getProperty("user.home"), ".config", "DemoXMLParser");                File configFile = new File(configPath.toString(), "myconfig.xml");                DocumentBuilderFactory factory =                DocumentBuilderFactory.newInstance();                DocumentBuilder builder = null;                               try {                        builder = factory.newDocumentBuilder();                } catch (ParserConfigurationException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                }                       Document doc = null;                       try {                        doc = builder.parse(configFile);                } catch (SAXException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                } catch (IOException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                }        doc.getDocumentElement().normalize();        Node updatePref = null;//        NodeList nodes = doc.getChildNodes();        NodeList nodes = doc.getElementsByTagName("window");        for (int i = 0; i < nodes.getLength(); i++) {           Node mynode = nodes.item(i);           System.out.println("Property = " + mynode.getNodeName());                      if (mynode.getNodeType() == Node.ELEMENT_NODE) {               Element myelement = (Element) mynode;               System.out.println("Theme = " + myelement.getElementsByTagName("theme").item(0).getTextContent());               System.out.println("Fullscreen = " + myelement.getElementsByTagName("fullscreen").item(0).getTextContent());               System.out.println("Icon set = " + myelement.getElementsByTagName("icons").item(0).getTextContent());               updatePref = myelement.getElementsByTagName("fullscreen").item(0);               updatePref.setTextContent("2");               System.out.println("Updated fullscreen to " + myelement.getElementsByTagName("fullscreen").item(0).getTextContent());                     } // close if                   }// close for        // write DOM back to the file        TransformerFactory transformerFactory = TransformerFactory.newInstance();        Transformer xtransform;        DOMSource mydom = new DOMSource(doc);        StreamResult streamResult = new StreamResult(configFile);        try {                xtransform = transformerFactory.newTransformer();                xtransform.transform(mydom, streamResult);        } catch (TransformerException e) {                e.printStackTrace();        }                           } // close method} //close class

如何保證配置不出問題

編寫配置檔案看上去是一個還挺簡單的任務。一開始,你可能會用一個簡單的文字格式,因為你的應用程式只要寥寥幾個配置項而已。但是,隨著你引入了更多的配置項,讀取或者寫入錯誤的資料可能會給你的應用程式帶來意料之外的錯誤。一種幫助你保持配置過程安全、不出錯的方法,就是使用類似 XML 的規範格式,然後依靠你用的程式語言的內建功能來處理這些複雜的事情。





來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69979876/viewspace-2886073/,如需轉載,請註明出處,否則將追究法律責任。

相關文章