使用 SnakeYAML 解析 YAML

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);

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/niugang0920/article/details/119530005