首页 > 应用开发 > Java > 使用 Java 解析XML文件
当您编写应用程序时,您通常希望用户自定义他们与应用程序的交互方式以及应用程序与系统的交互方式。这种方法通常称为“首选项”或“设置”,它们保存在“首选项文件”或“配置文件”中,有时简称为“配置配置”。配置文件可以有多种格式,包括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 的初学者,你可以先阅读我写的
面向 Java 入门开发者的 7 个小技巧。一旦你对 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 数据。
基本上就是这样啦。理论上来讲,你已经完成了数据解析的工作。可是,如果你不能够访问数据的话,数据解析也没有多少用处嘛。所以,就让我们再来写一些查询,从你的配置中读取重要的属性值吧。
从你已经读取的 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
用户时不时地会改变某个偏好项,这时候
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博客 ” ,链接:https://blog.itpub.net/69979876/viewspace-2886073/ ,如需转载,请注明出处,否则将追究法律责任。