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項目中找到。

附錄


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM