1. Overview
1.概述
An INI file is an initialization or configuration file for Windows or MS-DOS. They have plain text content which comprises key-value pairs in sections. While we may prefer to configure our applications with Java’s native .properties files or other formats, we may sometimes need to consume data from existing INI files.
INI文件是Windows或MS-DOS的初始化或配置文件。它们有纯文本内容,包括部分的键值对。虽然我们可能更喜欢用Java的本地.properties文件或其他格式来配置我们的应用程序,但有时我们可能需要从现有的INI文件中获取数据。
In this tutorial, we’ll look at a couple of libraries that can help us. We’ll also look at a way to populate a POJO with data from our INI files.
在本教程中,我们将看一下几个可以帮助我们的库。我们还将看看如何用我们的INI文件中的数据来填充POJO。
2. Creating a Sample INI File
2.创建一个INI文件样本
Let’s start with a sample INI file, sample.ini:
让我们从一个INI文件样本开始,sample.ini。
; for 16-bit app support
[fonts]
letter=bold
text-size=28
[background]
color=white
[RequestResult]
RequestCode=1
[ResponseResult]
ResultCode=0
This file has four sections, using a mixture of lower-case, kebab-case, and upper-camel-case for naming. It has values that are either strings or numeric.
这个文件有四个部分,混合使用小写字母、kebab字母和大写字母来命名。它的值是字符串或数字。
3. Parsing an INI File Using ini4j
3.使用ini4j解析一个INI文件
ini4j is a lightweight library for reading configuration out of INI files. It hasn’t been updated since 2015.
ini4j是一个轻量级的库,用于从INI文件中读取配置。自2015年以来,它一直没有被更新。
3.1. Installing ini4j
3.1.安装ini4j
To be able to use the ini4j library, first, we should add its dependency in our pom.xml:
为了能够使用ini4j库,首先,我们应该在我们的pom.xml中添加其依赖。
<dependency>
<groupId>org.ini4j</groupId>
<artifactId>ini4j</artifactId>
<version>0.5.4</version>
</dependency>
3.2. Opening an INI File in ini4j
3.2.在ini4j中打开一个INI文件
We can open an INI file in ini4j by constructing an Ini object:
我们可以通过构建一个Ini对象,在ini4j中打开一个INI文件。
File fileToParse = new File("sample.ini");
Ini ini = new Ini(fileToParse);
This object now contains the sections and keys.
这个对象现在包含章节和键。
3.3. Reading a Section Key
3.3.阅读章节键
We can read a key in a section from our INI file with the get() function on the Ini class:
我们可以通过Ini类上的get()函数从我们的INI文件中读取一个部分的键。
assertThat(ini.get("fonts", "letter"))
.isEqualTo("bold");
3.4. Converting to a Map
3.4.转化为地图
Let’s see how easy it is to convert the whole INI file into Map<String, Map<String, String>>, which is a Java native data structure that represents the hierarchy of an INI file:
让我们看看把整个INI文件转换成Map<String, Map<String, String>>是多么容易,它是一个Java本地数据结构,代表了INI文件的层次结构。
public static Map<String, Map<String, String>> parseIniFile(File fileToParse)
throws IOException {
Ini ini = new Ini(fileToParse);
return ini.entrySet().stream()
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
}
Here, the entrySet of the Ini object is essentially a key-value pair of String and Map<String, String>. The internal representation of Ini is pretty much a Map, so it’s easily converted into a plain Map by using stream() and the toMap() collector.
这里,Ini对象的entrySet本质上是一个String和Map<String, String>的键值对。Ini的内部表示几乎是一个Map,所以通过使用stream()和toMap()收集器,它很容易被转换成一个普通的Map。
We can now read sections from this map with get():
我们现在可以用get()从这个地图中读取部分内容。
assertThat(result.get("fonts").get("letter"))
.isEqualTo("bold");
The Ini class is very easy to use out of the box, and converting to a Map may not be necessary, though we’ll find a use for that later.
Ini类非常容易使用,转换为Map可能没有必要,尽管我们稍后会发现它的用途。
However, ini4j is an old library and does not look to be well-maintained. Let’s consider another option.
然而,ini4j是一个老的库,而且看起来维护得并不好。让我们考虑另一种选择。
4. Parsing an INI File Using Apache Commons
4.使用Apache Commons解析INI文件
Apache Commons has a more sophisticated tool for processing INI files. This is capable of modeling the whole file for read and write, though we’ll focus only on its parsing capabilities.
Apache Commons有一个更复杂的工具来处理INI文件。它能够对整个文件进行读写建模,不过我们只关注它的解析能力。
4.1. Installing Commons Configuration
4.1.安装Commons配置
Let’s start by adding the required dependency in our pom.xml:
让我们首先在我们的pom.xml中添加所需的依赖。
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-configuration2</artifactId>
<version>2.8.0</version>
</dependency>
Version 2.8.0 was updated in 2022, which is more recent than ini4j.
2.8.0版本是在2022年更新的,比ini4j更新。
4.2. Opening an INI File
4.2.打开一个INI文件
We can open an INI file by declaring an INIConfiguration object and passing it a Reader:
我们可以通过声明一个INIConfiguration对象并传递给它一个Reader来打开一个INI文件。
INIConfiguration iniConfiguration = new INIConfiguration();
try (FileReader fileReader = new FileReader(fileToParse)) {
iniConfiguration.read(fileReader);
}
Here we’ve used the try-with-resources pattern to open a FileReader and then asked the INIConfiguration object to read it with the read function.
这里我们使用try-with-resources模式来打开一个FileReader ,然后要求INIConfiguration对象用read函数来读取它。
4.3. Reading a Section Key
4.3.阅读章节键
The INIConfiguration class has a getSection() function to read a section and a getProperty() function on the returned object to read a key:
INIConfiguration类有一个getSection()函数来读取一个部分,并在返回的对象上有一个getProperty()函数来读取一个键。
String value = iniConfiguration.getSection("fonts")
.getProperty("letter")
.toString();
assertThat(value)
.isEqualTo("bold");
We should note that getProperty() returns Object rather than String, so needs to be converted to a String.
我们应该注意,getProperty()返回Object而不是String,所以需要转换为String。
4.4. Converting to Map
4.4.转换为地图
We can also convert the INIConfiguration into a Map as before. This is a bit more complex than with ini4j:
我们还可以像以前一样将INIConfiguration转换为Map。这比使用ini4j要复杂一些。
Map<String, Map<String, String>> iniFileContents = new HashMap<>();
for (String section : iniConfiguration.getSections()) {
Map<String, String> subSectionMap = new HashMap<>();
SubnodeConfiguration confSection = iniConfiguration.getSection(section);
Iterator<String> keyIterator = confSection.getKeys();
while (keyIterator.hasNext()) {
String key = keyIterator.next();
String value = confSection.getProperty(key).toString();
subSectionMap.put(key, value);
}
iniFileContents.put(section, subSectionMap);
}
To get all of the sections, we need to use getSections() to find their names. Then getSection() can give us each section.
为了获得所有的部分,我们需要使用getSections()来找到它们的名字。然后getSection()可以给我们每个部分。
We can then use the Iterator that provides all the keys for the section and use getProperty() with each to get the key-value pairs.
然后我们可以使用提供该部分所有键的Iterator,并对每个键使用getProperty(),以获得键值对。
While a Map is harder to produce here, the advantage of the plainer data structure is that we can hide the INI file parsing from other parts of the system. Or, we could convert the configuration into POJOs.
虽然Map在这里更难制作,但更简单的数据结构的好处是我们可以将INI文件的解析从系统的其他部分隐藏起来。或者,我们可以将配置转换为POJOs。
5. Convert INI File to POJO
5.将INI文件转换为POJO
We can use Jackson to convert our Map structure into a POJO. We can decorate our POJO with deserialization annotations to help Jackson make sense of the various naming conventions in the original INI file. A POJO is easier to use than any of the data structures we’ve seen so far.
我们可以使用Jackson来将我们的Map结构转换成POJO。我们可以用反序列化注释来装饰我们的POJO,以帮助Jackson理解原始INI文件中的各种命名规则。POJO比我们到目前为止看到的任何数据结构都更容易使用。
5.1. Import Jackson
5.1.进口Jackson
We need to add Jackson to our pom.xml:
我们需要将Jackson加入我们的pom.xml。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency>
5.2. Defining Some POJOs
5.2.定义一些POJO
The fonts section of our sample file uses kebab-case for its properties. Let’s define a class to represent that section:
我们的样本文件的字体部分的属性使用kebab-case。让我们定义一个类来表示该部分。
@JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class)
public static class Fonts {
private String letter;
private int textSize;
// getters and setters
}
Here we’ve used the JsonNaming annotation to describe the case used in the properties.
这里我们使用了JsonNaming注解来描述属性中使用的情况。
Similarly, the RequestResult section has properties that use upper-camel-case:
同样地,RequestResult部分的属性也使用大写字母。
@JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class)
public static class RequestResult {
private int requestCode;
// getters and setters
}
The section names themselves are a variety of cases, so we can declare each section in our parent object, using the JsonProperty annotation to show deviations from the default lower-camel-case naming:
节的名称本身就是各种情况,所以我们可以在我们的父对象中声明每个节,使用JsonProperty注解来显示与默认的小写焦糖字母命名的偏差。
public class MyConfiguration {
private Fonts fonts;
private Background background;
@JsonProperty("RequestResult")
private RequestResult requestResult;
@JsonProperty("ResponseResult")
private ResponseResult responseResult;
// getters and setters
}
5.3. Convert from Map to POJO
5.3.从地图到POJO的转换
Now we have the ability to use either of our libraries to read an INI file as a Map and the ability to model the contents of the file as a POJO, we can use a Jackson ObjectMapper to perform the conversion:
现在我们有能力使用我们的任何一个库将INI文件作为Map来读取,并将文件的内容作为POJO来建模,我们可以使用Jackson ObjectMapper来执行转换。
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Map<String, String>> iniKeys = parseIniFile(TEST_FILE);
MyConfiguration config = objectMapper.convertValue(iniKeys, MyConfiguration.class);
Let’s check that the whole file was loaded correctly:
让我们检查一下整个文件是否被正确加载。
assertThat(config.getFonts().getLetter()).isEqualTo("bold");
assertThat(config.getFonts().getTextSize()).isEqualTo(28);
assertThat(config.getBackground().getColor()).isEqualTo("white");
assertThat(config.getRequestResult().getRequestCode()).isEqualTo(1);
assertThat(config.getResponseResult().getResultCode()).isZero();
We should note that numeric properties, such as textSize and requestCode have been loaded into our POJO as numbers.
我们应该注意,数字属性,如textSize和requestCode已经作为数字加载到我们的POJO。
6. Comparison of Libraries and Approaches
6.图书馆和方法的比较
The ini4j library is very simple to use and is essentially a simple Map-like structure. However, this is an older library without regular updates.
ini4j库使用起来非常简单,本质上是一个简单的Map-类结构。然而,这是一个没有定期更新的旧库。
The Apache Commons solution is fuller featured and has regular updates but requires a little more work to use.
Apache Commons的解决方案功能更全面,并有定期更新,但使用起来需要更多的工作。
7. Conclusion
7.结语
In this article, we saw how to read INI files using a couple of open-source libraries. We saw how to read individual keys and how to iterate over the whole file to produce a Map.
在这篇文章中,我们看到了如何使用几个开源的库来读取INI文件。我们看到了如何读取单个键,以及如何在整个文件上迭代以产生一个Map。
Then we saw how we can convert from Map to POJO using Jackson.
然后我们看到了如何使用Jackson从Map转换为POJO。
As always, the example code is available over on GitHub.
像往常一样,示例代码可在GitHub上获得。