Merge pull request #24969 from lkxed/20210707-Parsing-config-files-with-Java

Translated 20210707 Parsing config files with Java.md
This commit is contained in:
Xingyu.Wang 2022-03-19 22:22:37 +08:00 committed by GitHub
commit 9fbd121223
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -2,82 +2,82 @@
[#]: via: (https://opensource.com/article/21/7/parsing-config-files-java)
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: translator: (lkxed)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
Parsing config files with Java
使用 Java 解析 XML 文件
======
Implement persistent settings when you're writing software in Java.
在你使用 Java 编写软件时实现持久化配置。
![Person drinking a hot drink at the computer][1]
When you write an application, you often want users to be able to configure how they interact with it and how it interacts with their system. These are commonly called "preferences" or "settings," and they're stored in "preference files" or "configuration files," or just "configs." There are many different formats for config files, including INI, JSON, YAML, and XML, and every language parses these languages differently. This article discusses some of the ways you can implement persistent settings when you're writing software in the [Java programming language][2].
当你编写一个应用时,你通常都会希望用户能够定制化他们和应用交互的方式,以及应用与系统进行交互的方式。这种方式通常被称为 “偏好” 或者 “设置”,它们被保存在一个 “偏好文件” 或者 “配置文件” 中,有时也直接简称为 “配置”。配置文件可以有很多种格式,包括 INI、JSON、YAML 和 XML。每一种编程语言解析这些格式的方式都不同。本文主要讨论当你在使用 [Java 编程语言][2] 来编写软件时,实现持久化配置的方式。
### Choose a format
### 选择一个格式
Writing configuration files is surprisingly flexible. I've kept configuration options in a simple comma-delimited text file, and I've kept options in highly detailed YAML or XML. The most important thing about configuration files is that they are consistent and predictable. This makes it easy for you to write code that can quickly and easily extract data from the configuration file, as well as save and update options when the user decides to make a change.
编写配置文件是一件相当复杂的事情。我曾经试过把配置项使用逗号分隔保存在一个文本文件里,也试过把配置项保存在非常详细的 YAML 和 XML 中。对于配置文件来说,最重要是要有一致性和规律性,它们使你可以简单快速地编写代码,从配置文件中解析出数据;同时,当用户决定要做出修改时,很方便地保存和更新配置。
There are [several popular formats for configuration files][3]. Java has libraries for most of the common configuration formats, but in this article, I'll use the XML format. For some projects, you might choose to use XML for its inherent ability to provide lots of metadata about the data it contains, while for others, you may choose to avoid it due to its verbosity. Java makes working with XML relatively easy because it includes robust XML libraries by default.
目前有 [几种流行的配置文件格式][3]。对于大多数常见的配置文件格式Java 都有对应的<ruby><rt>libraries</rt></ruby>。在本文中,我将使用 XML 格式。对于一些项目,你可能会选择使用 XML因为它的一个突出特点是能够为包含的数据提供大量相关的元数据而在另外一些项目中你可能会因为 XML 的冗长而不选择它。在 Java 中使用 XML 是非常容易的,因为它默认包含了许多健壮的 XML 库。
### XML basics
### XML 基础
XML is a big topic. Just one of the books I own about XML is over 700 pages. Fortunately, using XML doesn't require in-depth knowledge of all its many features. Like HTML, XML is a hierarchical markup language with opening and closing tags, which may contain zero or more data. Here's a sample snippet of XML:
讨论 XML 可是一个大话题。我有一本关于 XML 的书,它有超过 700 页的内容。幸运的是,使用 XML 并不需要非常了解它的诸多特性。就像 HTML 一样XML 是一个带有开始和结束标记的分层标记语言,每一个标记(标签)内可以包含零个或更多数据。下面是一个 XML 的简单示例片段:
```
&lt;xml&gt;
  &lt;node&gt;
    &lt;element&gt;Penguin&lt;/element&gt;
  &lt;/node&gt;
&lt;/xml&gt;
```xml
<xml>
  <node>
    <element>Penguin</element>
  </node>
</xml>
```
In this rather self-descriptive example, here are the terms that XML parsers use:
在这个 <ruby>自我描述的<rt>self-descriptive</rt></ruby> 例子中XML 解析器使用了以下几个概念:
* **Document:** The `<xml>` tag opens a _document_, and the `</xml>` tag closes it.
* **Node:** The `<node>` tag is a _node_.
* **Element:** The `<element>Penguin</element>`, from the first `<` to the last `>`, is an _element_.
* **Content:** In the `<element>` element, the string `Penguin` is the _content_.
* **<ruby>文档<rt>Document</rt><ruby>**`<xml>` 标签标志着一个 _文档_ 的开始,`</xml>` 标签标志着这个文档的结束。
* **<ruby>节点<rt>Node</rt></ruby>**`<node>` 标签代表了一个 _节点_
* **<ruby>元素<rt>Element</rt></ruby>**`<element>Penguin</element>` 中,从开头的 `<` 到最后的 `>` 表示了一个 _元素_
* **<ruby>内容<rt>Content</rt></ruby>** 在 `<element>` 元素里,字符串 `Penguin` 就是 _内容_
Believe it or not, that's all you need to know about XML to be able to write and parse it.
不管你信不信,只要了解了以上几个概念,你就可以开始编写、解析 XML 文件了。
### Create a sample config file
### 创建一个示例配置文件
A minimal example of a config file is all you need to learn how to parse XML. Imagine a config file tracking some display properties of a GUI window:
要学习如何解析 XML 文件,只需要一个极简的示例文件就够了。假设现在有一个配置文件,里面保存的是关于一个图形界面窗口的属性:
```
&lt;xml&gt;
  &lt;window&gt;
    &lt;theme&gt;Dark&lt;/theme&gt;
    &lt;fullscreen&gt;0&lt;/fullscreen&gt;
    &lt;icons&gt;Tango&lt;/icons&gt;
&lt;/window&gt;
&lt;/xml&gt;
```xml
<xml>
  <window>
    <theme>Dark</theme>
    <fullscreen>0</fullscreen>
    <icons>Tango</icons>
</window>
</xml>
```
Create a directory called `~/.config/DemoXMLParser`:
创建一个名为 `~/.config/DemoXMLParser` 的目录:
```
`$ mkdir ~/.config/DemoXMLParser`
```shell
$ mkdir ~/.config/DemoXMLParser
```
On Linux, the `~/.config` directory is the default configuration file location, as defined by the [Freedesktop][4] specification. If you're on an operating system that doesn't follow Freedesktop standards, you can still use this location, but you may have to create all the directories yourself.
在 Linux 中,`~/.config` 目录是存放配置文件的默认位置,这是在 [自由桌面工作组][4] 的规范中定义的。如果你正在使用一个不遵守 <ruby>自由桌面工作组<rt>Freedesktop</rt><ruby> 标准的操作系统,你也仍然可以使用这个目录,只不过你需要自己创建这些目录了。
Copy and paste the sample configuration XML into a file and save it as `~/.config/DemoXMLParser/myconfig.xml`.
复制 XML 的示例配置文件,粘贴并保存为 `~/.config/DemoXMLParser/myconfig.xml` 文件。
### Parse XML with Java
### 使用 Java 解析 XML
If you're new to Java, start by reading my [7 tips for new Java developers][5] article. Once you're relatively comfortable with Java, open your favorite integrated development environment (IDE) and create a new project. I call mine **myConfigParser**.
如果你是 Java 的初学者,你可以先阅读我写的 [面向 Java 入门开发者的 7 个小技巧][5]。一旦你对 Java 比较熟悉了打开你最喜爱的集成开发工具IDE创建一个新工程。我会把我的新工程命名为 **myConfigParser**
Without worrying too much about imports and error catching initially, you can instantiate a parser using the standard Java extensions found in the `javax` and `java.io` libraries. If you're using an IDE, you'll be prompted to import the appropriate libraries; otherwise, you can find a full list of libraries in the complete version of this code later in this article.
刚开始先不要太关注依赖导入和异常捕获这些,你可以先尝试用 `javax``java.io` 包里的标准 Java 扩展来实例化一个解析器。如果你使用了 IDE它会提示你导入合适的依赖。如果没有你也可以在文章稍后的部分找到完整的代码里面就有完整的依赖列表。
```
```java
Path configPath = Paths.get([System][6].getProperty("user.home"), ".config", "DemoXMLParser");
[File][7] configFile = new [File][7](configPath.toString(), "myconfig.xml");
@ -91,23 +91,22 @@ doc = builder.parse(configFile);
doc.getDocumentElement().normalize();
```
This example code uses the `java.nio.Paths` library to locate the user's home directory, adding the default configuration location to the path. Then it defines the configuration file to be parsed as a File object using the `java.io.File` library.
这段示例代码使用了 `java.nio.Paths` 类来找到用户的主目录,然后在拼接上默认配置文件的路径。接着,它用 `java.io.File` 类来把配置文件定义为一个 `File` 对象。
Next, it uses the `javax.xml.parsers.DocumentBuilder` and `javax.xml.parsers.DocumentBuilderFactory` libraries to create an internal document builder so that the Java program can ingest and parse XML data.
紧接着,它使用了 `javax.xml.parsers.DocumentBuilder``javax.xml.parsers.DocumentBuilderFactory` 这两个类来创建一个内部的文档构造器,这样 Java 程序就可以导入并解析 XML 数据了。
Finally, Java builds a document called `doc` and loads the `configFile` file into it. Using `org.w3c.dom` libraries, it normalizes the ingested XML data.
最后Java 创建一个叫 `doc` 的文档对象,并且把 `configFile` 文件加载到这个对象里。通过使用 `org.w3c.dom` 包,它读取并规范化了 XML 数据。
That's essentially it. Technically, you're done parsing the data. But parsed data isn't of much use to you if you can't access it, so write some queries to extract important values from your configuration.
基本上就是这样啦。理论上来讲,你已经完成了数据解析的工作。可是,如果你不能够访问数据的话,数据解析也没有多少用处嘛。所以,就让我们再来写一些查询,从你的配置中读取重要的属性值吧。
### Accessing XML values with Java
### 使用 Java 访问 XML 的值
Getting data from your ingested XML document is a matter of referencing a specific node and then "walking" through the elements it contains. It's common to use a series of loops to iterate through elements in nodes, but I'll keep that to a minimum here, just to keep the code easy to read:
从你已经读取的 XML 文档中获取数据,其实就是要先找到一个特定的节点,然后遍历它包含的所有元素。通常我们会使用多个循环语句来遍历节点中的元素,但是为了保持代码可读性,我会尽可能少地使用循环语句:
```
```java
NodeList nodes = doc.getElementsByTagName("window");
for (int i = 0; i &lt; nodes.getLength(); i++) {
for (int i = 0; i < nodes.getLength(); i++) {
 Node mynode = nodes.item(i);
 System.out.println("Property = " + mynode.getNodeName());
       
@ -121,16 +120,16 @@ for (int i = 0; i &lt; nodes.getLength(); i++) {
}
```
This sample code creates a `NodeList` object called `nodes` using the `org.w3c.dom.NodeList;` library. This object contains any child node with a name that matches the string `window`, which is the only node in the sample config file created in this article.
这段示例代码使用了 `org.w3c.dom.NodeList` 类,创建了一个名为 `nodes``NodeList` 对象。这个对象包含了所有名字匹配字符串 `window` 的子节点,实际上这样的节点只有一个,因为本文的示例配置文件中只配置了一个。
Next, it creates a for-loop to iterate over the `nodes` list, taking each node in order of appearance and processing it with an if-then loop. The if-then loop creates an `Element` object called `myelement` that contains all elements within the current node. You can query the elements using methods like `getChildNodes`, `getElementById`, and others, as [documented][9] by the project.
紧接着,它使用了一个 for 循环来遍历 `nodes` 列表。具体过程是:根据节点出现的顺序逐个取出,然后交给一个 `if-then` 子句处理。这个 `if-then` 子句创建了一个名为 `myelement``Element` 对象,其中包含了当前节点下的所有元素。你可以使用例如 `getChildNodes``getElementById` 方法来查询这些元素,项目中还 [记录了][9] 其他查询方法。
In this example, the elements are essentially the configuration keys. The values are stored as the content of the element, which you can extract with the `.getTextContent` method.
在这个示例中,每个元素就是配置的键。而配置的值储存在元素的内容中,你可以使用 `.getTextContent` 方法来提取出配置的值。
Run the code either in your IDE or as a binary:
在你的 IDE 中运行代码(或者运行编译后的二进制文件):
```
```java
$ java ./DemoXMLParser.java
Property = window
Theme = Dark
@ -138,10 +137,10 @@ Fullscreen = 0
Icon set = Tango
```
Here's the full code:
下面是完整的代码示例:
```
```java
package myConfigParser;
import java.io.File;
@ -187,7 +186,7 @@ public class ConfigParser {
        doc.getDocumentElement().normalize();
       
        NodeList nodes = doc.getElementsByTagName("window");
        for (int i = 0; i &lt; nodes.getLength(); i++) {
        for (int i = 0; i < nodes.getLength(); i++) {
           Node mynode = nodes.item(i);
           [System][6].out.println("Property = " + mynode.getNodeName());
           
@ -203,22 +202,22 @@ public class ConfigParser {
} //close class
```
### Updating XML with Java
### 使用 Java 更新 XML
From time to time, a user is going to change a preference. The `org.w3c.dom` libraries can update the contents of an XML element; you only have to select the XML element the same way you did when reading it. Instead of using the `.getTextContent` method, you use the `.setTextContent` method:
用户时不时地会改变某个偏好项,这时候 `org.w3c.dom` 库就可以帮助你更新某个 XML 元素的内容。你只需要选择这个 XML 元素,就像你读取它时那样。不过,此时你不再使用 `.getTextContent` 方法,而是使用 `.setTextContent` 方法。
```
```java
updatePref = myelement.getElementsByTagName("fullscreen").item(0);
updatePref.setTextContent("1");
[System][6].out.println("Updated fullscreen to " + myelement.getElementsByTagName("fullscreen").item(0).getTextContent());  
```
This changes the XML document in your application's memory, but it doesn't write the data back to the drive. Using a combination of `javax` and `w3c` libraries, you can place your ingested XML back into your configuration file:
这么做会改变应用程序内存中的 XML 文档,但是还没有把数据写回到磁盘上。配合使用 `javax``w3c` 库,你就可以把读取到的 XML 内容写回到配置文件中。
```
```java
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer xtransform;
@ -230,12 +229,12 @@ StreamResult streamResult = new StreamResult(configFile);
xtransform.transform(mydom, streamResult);
```
This silently overwrites the previous configuration file with transformed data.
这么做会没有警告地写入转换后的数据,并覆盖掉之前的配置。
Here's the full code, complete with the updater:
下面是完整的代码,包括更新 XML 的操作:
```
```java
package myConfigParser;
import java.io.File;
@ -289,7 +288,7 @@ public class ConfigParser {
        Node updatePref = null;
//        NodeList nodes = doc.getChildNodes();
        NodeList nodes = doc.getElementsByTagName("window");
        for (int i = 0; i &lt; nodes.getLength(); i++) {
        for (int i = 0; i < nodes.getLength(); i++) {
           Node mynode = nodes.item(i);
           [System][6].out.println("Property = " + mynode.getNodeName());
           
@ -325,11 +324,11 @@ public class ConfigParser {
} //close class
```
### Keep configuration trouble-free
### 如何保证配置不出问题
Configuration can be a deceptively simple routine. You might start with a simple plain text config format while your application has only a few configurable features, but as you introduce more options, reading or writing incorrect data can cause unexpected behavior from your application. One way to help keep your configuration process safe from failure is to use a strict format like XML and to lean on your programming language's built-in features to handle the complexity.
编写配置文件看上去是一个还挺简单的任务。一开始,你可能会用一个简单的文本格式,因为你的应用程序只要寥寥几个配置项而已。但是,随着你引入了更多的配置项,读取或者写入错误的数据可能会给你的应用程序带来意料之外的错误。一种帮助你保持配置过程安全、不出错的方法,就是使用类似 XML 的规范格式,然后依靠你用的编程语言的内置功能来处理这些复杂的事情。
I like using Java and XML for this very reason. When I try to read the wrong configuration value, Java lets me know, often because the node my code claims to want to read doesn't exist in the XML path I expect. XML's highly structured format helps me keep my code reliable, and that benefits both the users and the developer.
这也正是我喜欢使用 Java 和 XML 的原因。每当我试图读取错误的配置值时Java 就会提醒我。通常呢,这是由于我在代码中试图获取的节点,并不存在于我期望的 XML 路径中。XML 这种高度结构化的格式帮助了代码保持可靠性,这对用户和开发者来说都是有好处的。
--------------------------------------------------------------------------------
@ -337,7 +336,7 @@ via: https://opensource.com/article/21/7/parsing-config-files-java
作者:[Seth Kenlon][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
译者:[lkxed](https://github.com/lkxed)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出