1.介绍
如何使用 SnakeYAML 库将 Java 对象序列化为 YAML 文档,或YAML文档转为Java对象
2.依赖
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.21</version>
</dependency>
3.入口点
Yaml 类是 API 的入口点:
Yaml yaml = new Yaml();
由于实现不是线程安全的,不同的线程必须有自己的 Yaml 实例。
4.加载 YAML 文档
该库支持从 String 或 InputStream 加载文档。 示例将基于解析 InputStream。
首先定义一个简单的 YAML 文档, customer.yaml:
firstName: "张"
lastName: "三"
age: 20
4.1. 基本用法
使用 Yaml 类解析上面的 YAML 文档:
@Test
public void loadToMap() {
Yaml yaml = new Yaml();
InputStream inputStream = YamlTest1.class
.getClassLoader()
.getResourceAsStream("yaml/customer.yaml");
Map<String, Object> obj = yaml.load(inputStream);
System.out.println(obj);
}
输出
{firstName=张, lastName=三, age=20}
默认情况下, load() 方法返回一个 Map 实例。 每次查询 Map 对象都需要提前知道属性键名,而且遍历嵌套属性也不容易。
4.2. 自定义类型
该库还提供了一种将文档作为自定义类加载的方法。 此选项将允许轻松遍历内存中的数据。
定义一个 Customer 类并尝试再次加载文档:
@Data
public class Customer {
private String firstName;
private String lastName;
private int age;
}
YAML 文档被反序列化为已知类型,可以在文档中指定一个显式的全局标签。
新建customer_with_type.yaml文件
!!com.niugang.javabase.yaml.Customer
firstName: "李"
lastName: "四"
age: 20
请注意文档中的第一行,其中包含有关加载类时要使用的类的信息。
@Test
public void loadToObjectType() {
Yaml yaml = new Yaml();
InputStream inputStream = YamlTest1.class
.getClassLoader()
.getResourceAsStream("yaml/customer_with_type.yaml");
Customer customer = yaml.load(inputStream);
System.out.println(customer);
}
load() 方法现在返回一个 Customer 类型的实例。 这种方法的缺点是必须将类型指定到YAML文件中。
加载自定义类型的另一种方法是使用 Constructor 类。 通过这种方式,可以为要解析的 YAML 文档指定根类型。 创建一个以 Customer 类型为根类型的 Constructor 实例,并将其传递给 Yaml 实例。
现在加载 customer_no_type.yaml,获得 Customer 对象:
firstName: "李"
lastName: "四"
age: 20
@Test
public void loadToObjectType2() {
Yaml yaml = new Yaml(new Constructor(Customer.class));
InputStream inputStream = YamlTest1.class
.getClassLoader()
.getResourceAsStream("yaml/customer_no_type.yaml");
Customer customer = yaml.load(inputStream);
System.out.println(customer);
}
4.3. 隐式类型
如果没有为给定属性定义类型,库会自动将值转换为隐式类型。
例如:
1.0 -> Float
42 -> Integer
2009-03-30 -> Date
@Test
public void implicitTypes() {
Yaml yaml = new Yaml();
Map<Object, Object> document = yaml.load("3.0: 2021-08-07");
System.out.println(document);
//{3.0=Sat Aug 07 08:00:00 CST 2021}
}
4.4. 嵌套对象和集合
给定顶级类型,库会自动检测嵌套对象的类型,除非它们是接口或抽象类,并将文档反序列化为相关的嵌套类型。
customer_with_contact_details_and_address.yaml
firstName: "张"
lastName: "三"
age: 31
contactDetails:
- type: "mobile"
number: 123456789
- type: "landline"
number: 456786868
homeAddress:
line: "16号"
city: "西安"
state: "长安街"
zip: 345657
转化类
@Data
public class CustomerNest {
private String firstName;
private String lastName;
private int age;
private List<Contact> contactDetails;
private Address homeAddress;
}
@Data
public class Address {
private String line;
private String city;
private String state;
private Integer zip;
}
@Data
public class Contact {
private String type;
private int number;
}
@Test
public void nestedObjects() {
Yaml yaml = new Yaml(new Constructor(CustomerNest.class));
InputStream inputStream = YamlTest1.class
.getClassLoader()
.getResourceAsStream("yaml/customer_with_contact_details_and_address.yaml");
CustomerNest customer = yaml.load(inputStream);
System.out.println(customer);
}
单元测试
@Test
public void nestedObjects() {
Yaml yaml = new Yaml(new Constructor(CustomerNest.class));
InputStream inputStream = YamlTest.class
.getClassLoader()
.getResourceAsStream("yaml/customer_with_contact_details_and_address.yaml");
CustomerNest customer = yaml.load(inputStream);
System.out.println(customer);
}
4.5. 类型安全集合
当给定 Java 类的一个或多个属性是类型安全(通用)集合时,指定TypeDescription以便识别正确的参数化类型就很重要。
为了加载此文档,可以为顶级类的给定属性指定*TypeDescription* :
@Test
public void nestedObjects2() {
Constructor constructor = new Constructor(CustomerNest.class);
TypeDescription customTypeDescription = new TypeDescription(CustomerNest.class);
customTypeDescription.addPropertyParameters("contactDetails", Contact.class);
constructor.addTypeDescription(customTypeDescription);
Yaml yaml = new Yaml(constructor);
InputStream inputStream = YamlTest.class
.getClassLoader()
.getResourceAsStream("yaml/customer_with_contact_details_and_address.yaml");
CustomerNest customer = yaml.load(inputStream);
System.out.println(customer);
}
4.6. 加载多个文档
可能存在这样的情况,在单个文件中有多个 YAML 文档,想要解析所有这些文档。所述YAML类提供了一个LOADALL()方法来完成这种类型的解析。
默认情况下,该方法返回Iterable的实例,其中每个对象的类型为Map<String, Object>。 如果需要自定义类型,那可以使用 上面讨论的Constructor实例。
示例如下:
---
firstName: "张"
lastName: "三"
age: 20
---
firstName: "李"
lastName: "四"
age: 25
单元测试如下:
@Test
public void loadMore() {
Yaml yaml = new Yaml(new Constructor(Customer.class));
InputStream inputStream = this.getClass()
.getClassLoader()
.getResourceAsStream("yaml/customer_more.yaml");
int count = 0;
for (Object object : yaml.loadAll(inputStream)) {
count++;
System.out.println(object);
}
System.out.println(count);
}
5. 转储 YAML 文件
该库还提供了一种将给定 Java 对象转储到 YAML 文档中的方法。输出可以是字符串或指定的文件/流。
5.1. 基本用法
一个将Map<String, Object>实例转储到 YAML 文档 (String)的简单示例
/**
* 写入到字符串流中
*/
@Test
public void mapGenerateCorrectYAML() {
Map<String, Object> data = new LinkedHashMap<>();
data.put("name", "张三");
data.put("age", "25");
data.put("mobile", new String[] { "15012345678", "18745612378" });
Yaml yaml = new Yaml();
StringWriter writer = new StringWriter();
yaml.dump(data, writer);
System.out.println(writer.toString());
}
将数据写入yaml文件中
/**
* 写入到文件中
*/
@Test
public void mapGenerateCorrectYAMLFile() throws IOException {
Map<String, Object> data = new LinkedHashMap<>();
data.put("name", "张三");
data.put("age", "25");
data.put("mobile", new String[] { "15012345678", "18745612378" });
Yaml yaml = new Yaml();
FileWriter writer = new FileWriter("map.yaml");
yaml.dump(data, writer);
System.out.println(writer.toString());
}
5.2. 自定义 Java 对象
可以选择将自定义 Java 类型转储到输出流中或文件中
以下单元测试演示,从一个yaml读取java对象,写入另外一个yaml文件中
@Test
public void writerData() {
Yaml yamlWrite = new Yaml();
Yaml yamlRead = new Yaml(new Constructor(CustomerNest.class));
InputStream inputStream = YamlTest.class
.getClassLoader()
.getResourceAsStream("yaml/customer_with_contact_details_and_address.yaml");
CustomerNest customer = yamlRead.load(inputStream);
try {
FileWriter fileWriter = new FileWriter("test.yaml");
//写数据
yamlWrite.dump(customer, fileWriter);
} catch (IOException e) {
e.printStackTrace();
}
}
为了避免输出文件中的标签名,可以使用库提供的 dumpAs()方法,指定输出的标签。
所以在上面的代码中,可以调整以下内容以删除标签:
yaml.dumpAs(customer, Tag.MAP, null);