Java uses snake yaml to parse and serialize yaml

1. General

In this article, we will learn how to use the snake yaml library to convert yaml documents into Java objects and how Java objects are serialized into yaml documents.

2. Project settings

To use snake yaml in a project, you need to add Maven dependencies (the latest version can be found here):

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

3. Entry point

This yaml class is the entry point of the API:

Yaml yaml = new Yaml()

Since the implementation is not thread safe, different threads must have their own yaml instances.

4. Load yaml document

Snakeyaml supports loading documents from string or InputStream. We start by defining a simple yaml document and then naming the file customer yaml:

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

4.1。 Basic Usage

Now we will use the yaml class to parse the above yaml document:

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

The above code generates the following output:

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

By default, the load () method returns a map object. When querying a map object, we need to know the name of the property key in advance, otherwise it is easy to make mistakes. A better way is to customize the type.

4.2 user defined type resolution

Snake yaml provides a way to parse documents into custom types

Let's define a customer class and try to load the document again:

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

Now let's load:

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

Another way is to use the constructor:

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

4.3。 Implicit type

If no type is defined for the given attribute, the library automatically converts the value to an implicit type.

For example:

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

Let's use a testcase to test this implicit type conversion:

@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 nested objects

Snakeyaml supports nested complex types.

Let's add "contact information" and "address" details to "customer. Yaml" and save the new file as customer_ with_ contact_ details_ and_ address. yaml. 。

Now we will analyze the new yaml document:

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

Let's update the Java class:

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
}

Now, let's test yaml load ():

@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。 Type safe collection

When one or more properties of a given Java class are generic collection classes, you need to specify the generic type through typedescription so that it can be resolved correctly.

Let's assume that a customer has multiple contacts:

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

In order to resolve correctly, we can specify typedescription for the given attribute on the top-level class:

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。 Load multiple files

In some cases, there may be multiple yaml documents in a single file, and we want to parse all documents. The yaml class provides a LOADALL () method to complete this type of parsing.

Suppose the following is in a file:

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

We can use the LOADALL () method to parse the above content, as shown in the following code example:

@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. Generate yaml file

Snakeyaml supports serialization of Java objects into YML.

5.1。 Basic Usage

We will start with a simple example of dumping an instance of map < string, Object > into a yaml document (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());
}

The above code produces the following output (note that an instance using LinkedHashMap will preserve the order of the output data):

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

5.2。 Custom Java objects

We can also choose to dump custom Java types into the output stream.

@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());
}

The generated content will contain!! com. baeldung. snakeyaml. Customer, in order to avoid using tag names in the output file, we can use the dumpas () method provided by the library.

Therefore, in the above code, we can make the following adjustments to delete the tag:

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

Vi. conclusion

This article explains how the snake yaml library parses and serializes yaml documents.

All examples can be found in the GitHub project.

appendix

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>