Yaml 是目前常用的一种软件参数存储格式,用于应对软件运行时调整参数的需求。本文介绍将 yaml 文件解析成对象的几种方法。
YAML 小知识
YAML 的全称是“YAML Ain't Markup Language”,是一个对人类阅读友好的数据序列化格式,几乎所有编程语言都有支持。
YAML 的在册媒体类型是“application/yaml
”(RFC9512),常用的文件扩展名是yaml
或yml
。
本文的所有例子在java8
、org.yaml:snakeyaml:1.30
,spring-boot 2.7.18
环境下测试通过。
方式 1:Snak Yaml
Snake yaml 是在 Java 中解析 YAML 文件必用的库。本文介绍的其他方法也都依赖 Snak yaml。
Snake yaml 本身就支持将 yaml 转换成 java bean 规范的对象。
来看这个例子:
Yaml 文件
user:
name: "root"
age: 18
app:
name: "springboot"
version: "1.0.0"
复制代码
数据类
public class App {
public String name;
public String version;
}
public class User {
String name;
int age;
int birthYear;
//省略 get set
}
public class ConfigFile {
User user;
App app;
//省略get set;
}
复制代码
于是,可以使用Yaml.loadAs
方法来将数据转换成对象:
public class LoadYaml {
public static final Path application_yml = Paths.get("src/main/resources/application.yml");
Yaml yaml = new Yaml();
@Test
void yaml_to_dto() throws Exception {
ConfigFile config = yaml.loadAs(Files.newInputStream(application_yml), ConfigFile.class);
assertThat(config).isNotNull();
assertThat(config.user).isNotNull();
assertThat(config.user.getName()).isEqualTo("root");
assertThat(config.app).isNotNull();
}
}
复制代码
方式 2:Jackson
第二种方法是使用 jackson。jackson 加载 yaml 扩展后,就能支持从 yaml 到对象的转换。并且这样的好处是可以使用 jackson 的注解来调整映射行为,比直接使用Yaml.loadAs
功能要多。
@Test
void parseYaml() throws Exception {
ConfigFile config = new ObjectMapper(new YAMLFactory())
.readerFor(ConfigFile.class)
.readValue(application_yml.toFile());
assertThat(config).isNotNull();
assertThat(config.user).isNotNull();
assertThat(config.user.getName()).isEqualTo("root");
assertThat(config.app).isNotNull();
assertThat(config.app.name).isNotNull();
}
复制代码
提醒一下,有个缺陷,尚不支持含有 anchor 的 yaml 转换成对象。相应的问题跟踪:https://github.com/FasterXML/jackson-dataformats-text/issues/98
方式 3:Spring Boot
第三种方式是使用 spring boot 中提供的 bind 工具,好处是在类型的转换上更灵活,映射处理上更完善。
@Test
void parseYaml() throws Exception {
YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setResources(new FileSystemResource(application_yml));
Properties properties = factoryBean.getObject();
ConfigurationPropertySource propertySource = new MapConfigurationPropertySource(properties);
Binder binder = new Binder(propertySource);
User u = binder.bind("user", User.class).get();
assertThat(u.getName()).isEqualTo("root");
}
复制代码
注意 spring boot 默认不支持 public field bind。但支持 relax name bind,例如在 yaml 中写 birth-year: 2023
可以自动映射到 user.setBirthYear
方法上。
注意 2,spring boot 的处理逻辑与第 1、2 种是有明显差异的,前述 2 种方法大体上都是 tree→object 直接转换,但 spring boot 是 tree→properties→object,中间多了一步将 tree 展开成单层 hash map 的过程。
小结
Snake Yaml:支持 Java Bean,支持 public fields
Jackson:支持 Java Bean,支持 public fields,支持用注解调整对象构造
Spring Boot:支持 Java Bean,支持 relax name bind,支持类型转换
除了 yaml 以外,ini
、properties
、toml
都可以用来存储参数。除了文件存储以外,还可以使用配置中心(如 nacos),关系数据库也可以,根据需要而选择。
参考资料
https://yaml.org/https://www.baeldung.com/jackson-yaml
评论