1. 程式人生 > >JAVA使用SnakeYAML解析與序列化YAML

JAVA使用SnakeYAML解析與序列化YAML

1.概述

本文,我們將學習如何使用SnakeYAML庫將
YAML文件轉換為Java物件,以及JAVA物件如何序列化為YAML文件。

2.專案設定

要在專案中使用SnakeYAML,需要新增Maven依賴項(可在此處找到最新版本):

<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.25</version>
</dependency>

3.入口點

YAML類是API的入口點:

Yaml yaml = new Yaml()

由於實現不是執行緒安全的,因此不同的執行緒必須具有自己的Yaml例項。

4.載入YAML文件

SnakeYAML支援從StringInputStream載入文件,我們從定義一個簡單的YAML文件開始,然後將檔案命名為customer.yaml

firstName: "John"
lastName: "Doe"
age: 20

4.1。基本用法

現在,我們將使用Yaml類來解析上述YAML文件:

Yaml yaml = new Yaml();
InputStream inputStream = this.getClass()
  .getClassLoader()
  .getResourceAsStream("customer.yaml");
Map<String, Object> obj = yaml.load(inputStream);
System.out.println(obj);

上面的程式碼生成以下輸出:

{firstName=John, lastName=Doe, age=20}

預設情況下,load()方法返回一個Map物件。查詢Map物件時,我們需要事先知道屬性鍵的名稱,否則容易出錯。更好的辦法是自定義型別。

4.2自定義型別解析

SnakeYAML提供了一種將文件解析為自定義型別的方法

讓我們定義一個Customer類,然後嘗試再次載入該文件:

public class Customer {
 
    private String firstName;
    private String lastName;
    private int age;
 
    // getters and setters
}

現在我麼來載入:

Yaml yaml = new Yaml();
InputStream inputStream = this.getClass()
 .getClassLoader()
 .getResourceAsStream("customer.yaml");
Customer customer = yaml.load(inputStream);

還有一種方法是使用Constructor:

Yaml yaml = new Yaml(new Constructor(Customer.class));

4.3。隱式型別

如果沒有為給定屬性定義型別,則庫會自動將值轉換為隱式type。

例如:

1.0 -> Float
42 -> Integer
2009-03-30 -> Date

讓我們使用一個TestCase來測試這種隱式型別轉換:

@Test
public void whenLoadYAML_thenLoadCorrectImplicitTypes() {
   Yaml yaml = new Yaml();
   Map<Object, Object> document = yaml.load("3.0: 2018-07-22");
  
   assertNotNull(document);
   assertEquals(1, document.size());
   assertTrue(document.containsKey(3.0d));   
}

4.4 巢狀物件

SnakeYAML 支援巢狀的複雜型別。

讓我們向“ customer.yaml”新增“ 聯絡方式”  和“ 地址” 詳細資訊並將新檔案另存為customer_with_contact_details_and_address.yaml.

現在,我們將分析新的YAML文件:

firstName: "John"
lastName: "Doe"
age: 31
contactDetails:
   - type: "mobile"
     number: 123456789
   - type: "landline"
     number: 456786868
homeAddress:
   line: "Xyz, DEF Street"
   city: "City Y"
   state: "State Y"
   zip: 345657

我們來更新java類:

public class Customer {
    private String firstName;
    private String lastName;
    private int age;
    private List<Contact> contactDetails;
    private Address homeAddress;    
    // getters and setters
}

public class Contact {
    private String type;
    private int number;
    // getters and setters
}

public class Address {
    private String line;
    private String city;
    private String state;
    private Integer zip;
    // getters and setters
}

現在,我們來測試下Yamlload()

@Test
public void
  whenLoadYAMLDocumentWithTopLevelClass_thenLoadCorrectJavaObjectWithNestedObjects() {
  
    Yaml yaml = new Yaml(new Constructor(Customer.class));
    InputStream inputStream = this.getClass()
      .getClassLoader()
      .getResourceAsStream("yaml/customer_with_contact_details_and_address.yaml");
    Customer customer = yaml.load(inputStream);
  
    assertNotNull(customer);
    assertEquals("John", customer.getFirstName());
    assertEquals("Doe", customer.getLastName());
    assertEquals(31, customer.getAge());
    assertNotNull(customer.getContactDetails());
    assertEquals(2, customer.getContactDetails().size());
     
    assertEquals("mobile", customer.getContactDetails()
      .get(0)
      .getType());
    assertEquals(123456789, customer.getContactDetails()
      .get(0)
      .getNumber());
    assertEquals("landline", customer.getContactDetails()
      .get(1)
      .getType());
    assertEquals(456786868, customer.getContactDetails()
      .get(1)
      .getNumber());
    assertNotNull(customer.getHomeAddress());
    assertEquals("Xyz, DEF Street", customer.getHomeAddress()
      .getLine());
}

4.5。型別安全的集合

當給定Java類的一個或多個屬性是泛型集合類時,需要通過TypeDescription來指定泛型型別,以以便可以正確解析。

讓我們假設一個 一個Customer擁有多個Contact

firstName: "John"
lastName: "Doe"
age: 31
contactDetails:
   - { type: "mobile", number: 123456789}
   - { type: "landline", number: 123456789}

為了能正確解析,我們可以在頂級類上為給定屬性指定TypeDescription 

Constructor constructor = new Constructor(Customer.class);
TypeDescription customTypeDescription = new TypeDescription(Customer.class);
customTypeDescription.addPropertyParameters("contactDetails", Contact.class);
constructor.addTypeDescription(customTypeDescription);
Yaml yaml = new Yaml(constructor);

4.6。載入多個檔案

在某些情況下,單個檔案中可能有多個YAML文件,而我們想解析所有文件。所述YAML類提供了一個LOADALL()方法來完成這種型別的解析。

假設下面的內容在一個檔案中:

---
firstName: "John"
lastName: "Doe"
age: 20
---
firstName: "Jack"
lastName: "Jones"
age: 25

我們可以使用loadAll()方法解析以上內容,如以下程式碼示例所示:

@Test
public void whenLoadMultipleYAMLDocuments_thenLoadCorrectJavaObjects() {
    Yaml yaml = new Yaml(new Constructor(Customer.class));
    InputStream inputStream = this.getClass()
      .getClassLoader()
      .getResourceAsStream("yaml/customers.yaml");
 
    int count = 0;
    for (Object object : yaml.loadAll(inputStream)) {
        count++;
        assertTrue(object instanceof Customer);
    }
    assertEquals(2,count);
}

5.生成YAML檔案

SnakeYAML 支援 將java物件序列化為yml。

5.1。基本用法

我們將從一個將Map <String,Object>的例項轉儲到YAML文件(String)的簡單示例開始:

@Test
public void whenDumpMap_thenGenerateCorrectYAML() {
    Map<String, Object> data = new LinkedHashMap<String, Object>();
    data.put("name", "Silenthand Olleander");
    data.put("race", "Human");
    data.put("traits", new String[] { "ONE_HAND", "ONE_EYE" });
    Yaml yaml = new Yaml();
    StringWriter writer = new StringWriter();
    yaml.dump(data, writer);
    String expectedYaml = "name: Silenthand Olleander\nrace: Human\ntraits: [ONE_HAND, ONE_EYE]\n";
 
    assertEquals(expectedYaml, writer.toString());
}

上面的程式碼產生以下輸出(請注意,使用LinkedHashMap的例項將保留輸出資料的順序):

name: Silenthand Olleander
race: Human
traits: [ONE_HAND, ONE_EYE]

5.2。自定義Java物件

我們還可以選擇將自定義Java型別轉儲到輸出流中。

@Test
public void whenDumpACustomType_thenGenerateCorrectYAML() {
    Customer customer = new Customer();
    customer.setAge(45);
    customer.setFirstName("Greg");
    customer.setLastName("McDowell");
    Yaml yaml = new Yaml();
    StringWriter writer = new StringWriter();
    yaml.dump(customer, writer);        
    String expectedYaml = "!!com.baeldung.snakeyaml.Customer {age: 45, contactDetails: null, firstName: Greg,\n  homeAddress: null, lastName: McDowell}\n";
 
    assertEquals(expectedYaml, writer.toString());
}

生成內容會包含!!com.baeldung.snakeyaml.Customer,為了避免在輸出檔案中使用標籤名,我們可以使用庫提供的  dumpAs()方法。

因此,在上面的程式碼中,我們可以進行以下調整以刪除標記:

yaml.dumpAs(customer, Tag.MAP, null);

六 結語

本文說明了SnakeYAML庫解析和序列化YAML文件。

所有示例都可以在GitHub專案中找到。

附錄

  • 英文原文: Parsing YAML with SnakeYAML

作者:Jadepeng
出處:jqpeng的技術記事本--http://www.cnblogs.com/xiaoqi
您的支援是對博主最大的鼓勵,感謝您的認真閱讀。
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。