概述
INI文件是Windows或MS-DOS系统中的初始化或配置文件,本质是包含分节键值对的纯文本文件。虽然我们更习惯用Java原生的.properties
文件或其他格式配置应用,但有时仍需处理现有的INI文件。
本文将介绍两个实用的解析库,并演示如何将INI文件数据填充到POJO对象中。踩过坑的都知道,直接处理INI文件比想象中麻烦,但用对工具就能简单粗暴解决。
创建示例INI文件
先看个示例文件sample.ini
:
; for 16-bit app support
[fonts]
letter=bold
text-size=28
[background]
color=white
[RequestResult]
RequestCode=1
[ResponseResult]
ResultCode=0
这个文件包含四个节,命名混用了小写、kebab-case和大驼峰法,值类型包括字符串和数字。
使用ini4j解析INI文件
ini4j是个轻量级INI文件解析库,但要注意它自2015年后就没更新过。
安装ini4j
在pom.xml
中添加依赖:
<dependency>
<groupId>org.ini4j</groupId>
<artifactId>ini4j</artifactId>
<version>0.5.4</version>
</dependency>
在ini4j中打开INI文件
直接构造Ini
对象即可打开文件:
File fileToParse = new File("sample.ini");
Ini ini = new Ini(fileToParse);
这个对象现在包含了所有节和键。
读取节中的键
通过Ini
类的get()
方法读取键值:
assertThat(ini.get("fonts", "letter"))
.isEqualTo("bold");
转换为Map
将整个INI文件转为Map<String, Map<String, String>>
(天然匹配INI结构的Java原生数据结构):
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));
}
Ini
对象的entrySet
本质是String
和Map<String, String>
的键值对,其内部实现接近Map
,用流式处理轻松转换:
assertThat(result.get("fonts").get("letter"))
.isEqualTo("bold");
虽然Ini
类开箱即用,转Map操作可能多余,但后面会用到它的价值。不过要注意,ini4j是个老库,维护状况堪忧,我们看看更现代的替代方案。
使用Apache Commons解析INI文件
Apache Commons提供了更成熟的INI文件处理方案,支持完整的读写操作,本文只关注解析功能。
安装Commons Configuration
在pom.xml
添加依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-configuration2</artifactId>
<version>2.8.0</version>
</dependency>
2.8.0版本更新于2022年,比ini4j新得多。
打开INI文件
创建INIConfiguration
对象并传入Reader
:
INIConfiguration iniConfiguration = new INIConfiguration();
try (FileReader fileReader = new FileReader(fileToParse)) {
iniConfiguration.read(fileReader);
}
这里用了try-with-resources模式打开FileReader
,再调用read
方法读取内容。
读取节中的键
通过getSection()
获取节,再用getProperty()
读取键值:
String value = iniConfiguration.getSection("fonts")
.getProperty("letter")
.toString();
assertThat(value)
.isEqualTo("bold");
注意getProperty()
返回Object
而非String
,需手动转换类型。
转换为Map
转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);
}
操作步骤:
- 用
getSections()
获取所有节名 - 通过
getSection()
获取每个节对象 - 用
Iterator
遍历节内所有键 - 调用
getProperty()
获取键值对
虽然转Map稍麻烦,但这样能隐藏INI解析细节,方便后续转换为POJO。
将INI文件转换为POJO
用Jackson将Map结构转为POJO。通过注解处理INI文件中的各种命名风格,比直接操作Map更优雅。
引入Jackson
添加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>
定义POJO类
示例文件中的fonts
节使用kebab-case命名:
@JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class)
public static class Fonts {
private String letter;
private int textSize;
// getters and setters
}
用@JsonNaming
注解指定命名策略。类似地,RequestResult
节使用大驼峰法:
@JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class)
public static class RequestResult {
private int requestCode;
// getters and setters
}
节名本身命名风格不一,用@JsonProperty
处理差异:
public class MyConfiguration {
private Fonts fonts;
private Background background;
@JsonProperty("RequestResult")
private RequestResult requestResult;
@JsonProperty("ResponseResult")
private ResponseResult responseResult;
// getters and setters
}
从Map转换为POJO
现在结合前述库和Jackson的ObjectMapper
:
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Map<String, String>> iniKeys = parseIniFile(TEST_FILE);
MyConfiguration config = objectMapper.convertValue(iniKeys, MyConfiguration.class);
验证完整加载:
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();
注意textSize
和requestCode
等数值属性已自动转为数字类型。
库和方法的比较
对比维度 | ini4j | Apache Commons Configuration |
---|---|---|
易用性 | 极简,类似Map操作 | 功能完整但稍复杂 |
维护状态 | 停滞(2015年后未更新) | 活跃(2022年有更新) |
转换Map复杂度 | 一行流式处理搞定 | 需手动迭代处理 |
适用场景 | 简单配置快速解析 | 需要完整配置管理能力的场景 |
结论
本文通过两个开源库演示了INI文件的解析方法:
- 基本操作:读取单个键值和遍历全文件生成Map
- 进阶技巧:用Jackson将Map转换为强类型POJO
代码示例已上传至GitHub,实际使用时根据项目需求选库即可——简单需求用ini4j,长期维护的项目优先考虑Apache Commons。