<h4>選擇一個格式</h4>
<p>撰寫設定檔是一件相當複雜的事情。我曾經試過把設定項使用逗號分隔保存在一個文字檔案裡,也試過把設定項保存在非常詳細的 YAML 和 XML 中。對於設定檔來說,最重要是要有一致性和規律性,它們使你可以簡單快速地編寫程式碼,從設定檔中解析出資料;同時,當使用者決定要做出修改時,很方便地保存和更新配置。 </p>
<p>目前有 幾種流行的設定檔格式。對於大多數常見的設定檔格式,Java 都有對應的函式庫library。在本文中,我將使用 XML 格式。對於某些項目,你可能會選擇使用 XML,因為它的一個突出特點是能夠為包含的數據提供大量相關的元數據,而在其他一些項目中,你可能會因為 XML 的冗長而不選擇它。在 Java 中使用 XML 是非常容易的,因為它預設包含了許多健壯的 XML 程式庫。 </p>
<h4>XML 基礎</h4>
<p>討論 XML 可是一個大話題。我有一本關於 XML 的書,它有超過 700 頁的內容。幸運的是,使用 XML 並不需要非常了解它的許多特性。就像 HTML 一樣,XML 是一個帶有開始和結束標記的分層標記語言,每個標記(標籤)內可以包含零個或更多資料。以下是一個XML 的簡單範例片段:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:xml;"><xml>
<node>
<element>Penguin</element>
</node>
</xml></pre><div class="contentsignin">登入後複製</div></div><p>在這個 自我描述的self-descriptive 範例中,XML 解析器使用了以下幾個概念:</p><ul class=" list-paddingleft-2"><li><p>文檔Document:<code><xml></code> 標籤標誌著一個 文檔 的開始,<code></xml></code> 標籤標誌著這個文件的結束。 </p></li><li><p>節點Node:<code><node></code> 標籤代表了一個 節點。 </p></li><li><p>元素Element:<code><element>Penguin</element></code> 中,從開頭的 <code><</code> 到最後的 <code>> ;</code> 表示了一個 元素。 </p></li><li><p>內容Content: 在 <code><element></code> 元素裡,字串 <code>Penguin</code> 就是 內容。 </p></li></ul><p>不管你信不信,只要了解了以上幾個概念,你就可以開始寫、解析 XML 檔了。 </p><h4>建立一個範例設定檔</h4><p>要學習如何解析 XML 文件,只需要一個極簡的範例文件就夠了。假設現在有一個設定文件,裡面保存的是關於一個圖形介面視窗的屬性:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:xml;"><xml>
<window>
<theme>Dark</theme>
<fullscreen>0</fullscreen>
<icons>Tango</icons>
</window>
</xml></pre><div class="contentsignin">登入後複製</div></div><p>建立一個名為 <code>~/.config/DemoXMLParser</code> 的目錄:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:bash;">$ mkdir ~/.config/DemoXMLParser</pre><div class="contentsignin">登入後複製</div></div><p>在Linux 中,<code>~/.config</code> 目錄是存放設定檔的預設位置,這是在 自由桌面工作組 的規格中定義的。如果你正在使用一個不遵守 自由桌面工作小組Freedesktop標準的作業系統,你也仍然可以使用這個目錄,只不過你需要自己創建這些目錄了。 </p><p>複製 XML 的範例設定文件,貼上並儲存為 <code>~/.config/DemoXMLParser/myconfig.xml</code> 檔案。 </p><h4>使用 Java 解析 XML</h4><p>如果你是 Java 的初學者,你可以先閱讀我寫的 針對 Java 入門開發者的 7 個小技巧。一旦你對 Java 比較熟悉了,打開你最喜愛的整合開發工具(IDE),創建一個新工程。我會把我的新工程命名為 <code>myConfigParser</code>。 </p><p>剛開始先不要太專注於依賴導入和異常捕獲這些,你可以先嘗試用 <code>javax</code> 和 <code>java.io</code> 包裡的標準Java 擴充來實例化一個解析器。如果你使用了 IDE,它會提示你匯入適當的依賴。如果沒有,你也可以在文章稍後的部分找到完整的程式碼,裡面就有完整的依賴清單。 </p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:java;">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();</pre><div class="contentsignin">登入後複製</div></div><p>這段範例程式碼使用了 <code>java.nio.Paths</code> 類別來找到使用者的主目錄,然後在拼接上預設設定檔的路徑。接著,它用 <code>java.io.File</code> 類別來定義設定檔為一個 <code>File</code> 物件。 </p><p>緊接著,它使用了 <code>javax.xml.parsers.DocumentBuilder</code> 和 <code>javax.xml.parsers.DocumentBuilderFactory</code> 這兩個類別來建立一個內部的文件建構器,這樣Java 程式就可以匯入並解析XML 資料了。 </p><p>最後,Java 建立一個叫 <code>doc</code> 的文檔對象,並且把 <code>configFile</code> 檔案載入到這個物件裡。透過使用 <code>org.w3c.dom</code> 套件,它讀取並標準化了 XML 資料。 </p><p>基本上就是這樣啦。理論上來講,你已經完成了資料解析的工作。可是,如果你無法存取資料的話,資料解析也沒有多少用處嘛。所以,就讓我們再來寫一些查詢,從你的配置中讀取重要的屬性值吧。 </p><h4>使用 Java 访问 XML 的值</h4><p>从你已经读取的 XML 文档中获取数据,其实就是要先找到一个特定的节点,然后遍历它包含的所有元素。通常我们会使用多个循环语句来遍历节点中的元素,但是为了保持代码可读性,我会尽可能少地使用循环语句:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:java;">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());
}
}</pre><div class="contentsignin">登入後複製</div></div><p>这段示例代码使用了 <code>org.w3c.dom.NodeList</code> 类,创建了一个名为 <code>nodes</code> 的 <code>NodeList</code> 对象。这个对象包含了所有名字匹配字符串 <code>window</code> 的子节点,实际上这样的节点只有一个,因为本文的示例配置文件中只配置了一个。</p><p>紧接着,它使用了一个 <code>for</code> 循环来遍历 <code>nodes</code> 列表。具体过程是:根据节点出现的顺序逐个取出,然后交给一个 <code>if-then</code> 子句处理。这个 <code>if-then</code> 子句创建了一个名为 <code>myelement</code> 的 <code>Element</code> 对象,其中包含了当前节点下的所有元素。你可以使用例如 <code>getChildNodes</code> 和 <code>getElementById</code> 方法来查询这些元素,项目中还 记录了 其他查询方法。</p><p>在这个示例中,每个元素就是配置的键。而配置的值储存在元素的内容中,你可以使用 <code>.getTextContent</code> 方法来提取出配置的值。</p><p>在你的 IDE 中运行代码(或者运行编译后的二进制文件):</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:java;">$ java ./DemoXMLParser.java
Property = window
Theme = Dark
Fullscreen = 0
Icon set = Tango</pre><div class="contentsignin">登入後複製</div></div><p>下面是完整的代码示例:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:java;">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</pre><div class="contentsignin">登入後複製</div></div><h4>使用 Java 更新 XML</h4><p>用户时不时地会改变某个偏好项,这时候 <code>org.w3c.dom</code> 库就可以帮助你更新某个 XML 元素的内容。你只需要选择这个 XML 元素,就像你读取它时那样。不过,此时你不再使用 <code>.getTextContent</code> 方法,而是使用 <code>.setTextContent</code> 方法。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:java;">updatePref = myelement.getElementsByTagName("fullscreen").item(0);
updatePref.setTextContent("1");
System.out.println("Updated fullscreen to " + myelement.getElementsByTagName("fullscreen").item(0).getTextContent());</pre><div class="contentsignin">登入後複製</div></div><p>这么做会改变应用程序内存中的 XML 文档,但是还没有把数据写回到磁盘上。配合使用 <code>javax</code> 和 <code>w3c</code> 库,你就可以把读取到的 XML 内容写回到配置文件中。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:java;">TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer xtransform;
xtransform = transformerFactory.newTransformer();
DOMSource mydom = new DOMSource(doc);
StreamResult streamResult = new StreamResult(configFile);
xtransform.transform(mydom, streamResult);</pre><div class="contentsignin">登入後複製</div></div><p>这么做会没有警告地写入转换后的数据,并覆盖掉之前的配置。</p><p>下面是完整的代码,包括更新 XML 的操作:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:java;">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</pre><div class="contentsignin">登入後複製</div></div><h4>如何保证配置不出问题</h4>
<p>编写配置文件看上去是一个还挺简单的任务。一开始,你可能会用一个简单的文本格式,因为你的应用程序只要寥寥几个配置项而已。但是,随着你引入了更多的配置项,读取或者写入错误的数据可能会给你的应用程序带来意料之外的错误。一种帮助你保持配置过程安全、不出错的方法,就是使用类似 XML 的规范格式,然后依靠你用的编程语言的内置功能来处理这些复杂的事情。</p>
<p>这也正是我喜欢使用 Java 和 XML 的原因。每当我试图读取错误的配置值时,Java 就会提醒我。通常,这是由于我在代码中试图获取的节点,并不存在于我期望的 XML 路径中。XML 这种高度结构化的格式帮助了代码保持可靠性,这对用户和开发者来说都是有好处的。</p>
以上是Java持久化XML檔怎麼配置的詳細內容。更多資訊請關注PHP中文網其他相關文章!