Reading and Writing YAML Files in Java with SnakeYAML

Introduction

YAML stands for YAML Ain't Markup Language, it is a data-serialization language most commonly used for specifying project configuration details. The main motivation behind YAML is that it is designed to be in a format which is humanly friendly. With a glance we can get an understanding of the properties and their respective values, and also the relationship between properties if it exists.

As the YAML files are now being frequently used, almost in every other project we come across a scenario where we have to manage data in YAML files through our code. There are a lot of open-source libraries available for handling YAML files in Java.

To achieve this, we can use either of the two popular libraries: Jackson or SnakeYAML.

In this article, we'll be focusing on How to Read and Write YAML Files in Java with SnakeYAML.

SnakeYAML

SnakeYAML is a YAML-parsing library with a high-level API for serialization and deserialization of YAML documents.

The entry point for SnakeYAML is the Yaml class, similar to how the ObjectMapper class is the entry point in Jackson.

Loading documents can be done for individual documents via the load() method, or in batch via the loadAll() method. The methods accept an InputStream, which is a common format to encounter files in, as well as String objects containing valid YAML data.

On the other hand, we can dump() Java objects into YAML documents with ease - where the keys/fields and values are mapped into a document.

Naturally, SnakeYAML works well with Java Maps, given the <key>:<value> structure, however, you can work with custom Java objects as well.

If you're using Maven, install SnakeYAML by adding the following dependency:

<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>${org.snakeyaml.version}</version>
</dependency>

And if you're using Gradle, installing SnakeYAML is as simple as including the following in your Gradle file:

compile group: 'org.yaml', name: 'snakeyaml', version: '{version}'

You can check out the latest version of the library on Maven Central Repository.

Reading YAML with SnakeYAML

SnakeYAML allows you to read a YAML file into a simple Map object or parse the file and convert it into a custom Java object. Depending on your requirements you can decide in which format you want to read your YAML files. Let's take a look at both approaches.

Read YAML File as Map in Java

Let's start by reading a simple YAML file as a set of key-value pairs. The file we will be reading will have the following data:

id: 20
name: Bruce
year: 2020
address: Gotham City
department: Computer Science

Let's assume that we have this YAML in our Java project's resources folder. Let's load the file in as an InputStream first.

Then, we'll construct the Yaml instance, which is the entry point to using the library. The Yaml instance introduces us to methods, such as load() which allow us to read and parse any InputStream, Reader or String with valid YAML data:

InputStream inputStream = new FileInputStream(new File("student.yml"));

Yaml yaml = new Yaml();
Map<String, Object> data = yaml.load(inputStream);
System.out.println(data);

The method returns a Java Map in which the name of the properties are used as keys against their respective values.

Note that the values in the Map are of type Object, because in a YAML file - we can have our data as string values, numbers or even collections. All of these can be fit into an Object so it encompasses any value we might put in.

If we print our data object in which we have loaded the YAML file we will get the following result:

{id=20, name=Bruce, year=2020, address=Gotham City, department=Computer Science}

As you can see, the properties from the YAML file are simply mapped as key-value pairs in a Java Map object.

Let’s update our YAML file to contain collection data as well. The update YAML file look like this:

id: 20
name: Bruce
year: 2020
address: Gotham City
department: Computer Science
courses:
  - name: Algorithms
    credits: 6
  - name: Data Structures
    credits: 5
  - name: Design Patterns
    credits: 3

Now our YAML file contains a collection courses which has multiple data values.

To read the updated YAML file there is no need to update our Java code. Our previous code will be able to successfully load the YAML file into our Map object. After reading the file the result be:

{
 id=20, name=Bruce, year=2020, address=Gotham City, department=Computer Science, 
 courses=[{name=Algorithms, credits=6}, {name=Data Structures, credits=5}, {name=Design Patterns, credits=3}]
}

The courses element in the YAML file is read as an ArrayList where each value in the list is a Map object itself.

Read YAML Object as Custom Java Object

Now that we have successfully consumed the YAML file in our Java code as simple key-value pairs, let's load the same file as a custom Java object, which is a much more common use-case.

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

We will use the following Java classes to load data into from our YAML files:

public class Person {
    private long id;
    private String name;
    private String address;
    // Getters and setters
}

public class Student extends Person {
    private int year;
    private String department;
    private List<Course> courses;
    // Getters and setters
}

public class Course {
    private String name;
    private double credits;
    // Getters and setters
}

We will load the data into a Student object, where the courses element from the YAML file will be converted into a List of type Course.

We will use the same YAML file which we have used in the previous example and load it in as an InputStream:

InputStream inputStream = new FileInputStream(new File("student_with_courses.yml"));
Yaml yaml = new Yaml(new Constructor(Student.class));
Student data = yaml.load(inputStream);
System.out.println(data);

Now when we are creating our Yaml class object, we are specifying the data type we want to cast the data into. The new Constructor(Student.class) tells SnakeYAML to read the data from the YAML file and map it to our Student object.

The mapping is straightforward and the names of your object attributes will have to match the names of the YAML attributes (courses -> courses).

This results in:

Student[Person[id=20, name='Bruce', address='Gotham City'], year=2020, department='Computer Science', courses=[Course[name='Algorithms', credits=6.0], Course[name='Data Structure', credits=5.0], Course[name='Design pattern', credits=3.0]]]

As you can see SnakeYAML has successfully created the Student object while keeping the Student class inheritance (Parent class Person) and association with the Course class intact.

Writing YAML with SnakeYAML

Now that we have successfully read YAML files in our Java code, lets start writing data into YAML files using our Java project. Similar to reading YAML documents, we can write simple Java Map and a custom Java object into a YAML file.

Write Map Into YAML

Lets first write a simple Map object to a YAML file:

Map<String, Object> dataMap = new HashMap<>();
dataMap.put("id", 19);
dataMap.put("name", "John");
dataMap.put("address", "Star City");
dataMap.put("department", "Medical");

Now, let's create a new PrintWriter object, with the output directory in mind, and dump() the dataMap using that writer.

Note: The dump() method accepts any Writer object:

PrintWriter writer = new PrintWriter(new File("./src/main/resources/student_output.yml"));
Yaml yaml = new Yaml();
yaml.dump(dataMap, writer);

This results in a file that contains:

{address: Star City, name: John, id: 19, department: Medical}

Note: is that the output YAML file does not have the values in the same sequence in which we added them in our Java Map object, since we've used a HashMap which doesn't preserve the order of entry.

You can fix this issue by using a LinkedHashMap instead.

Write Custom Java Object into YAML

Now let's try and save our Student class in YAML format in the output file. For this we will use following code to setup the Student object:

Student student = new Student();

student.setId(21);
student.setName("Tim");
student.setAddress("Night City");
student.setYear(2077);
student.setDepartment("Cyberware");

Course courseOne = new Course();
courseOne.setName("Intelligence");
courseOne.setCredits(5);

Course courseTwo = new Course();
courseTwo.setName("Crafting");
courseTwo.setCredits(2);

List<Course> courseList = new ArrayList<>();
courseList.add(courseOne);
courseList.add(courseTwo);

student.setCourses(courseList);

Now, let's use our Yaml instance with a Writer implementation to dump() the data into a file:

PrintWriter writer = new PrintWriter(new File("./src/main/resources/student_output_bean.yml"));
Yaml yaml = new Yaml();
yaml.dump(student, writer);

This results in:

!!model.Student
address: Night City
courses:
- {credits: 5.0, name: Intelligence}
- {credits: 2.0, name: Crafting}
department: Cyberware
id: 21
name: Tim
year: 2077

If you take a closer look at the YAML output files generated by our code, you will see that in the first example, all the data was dumped in a single line whereas in the second example the Course object values are written in a single line each under the courses element.

Although both the generated output files have valid YAML syntax, if you want to create a YAML file in the more commonly used format where each value is written on a single line and there are not parentheses, you can tweak the DumperOptions object, and pass it into the Yaml constructor:

DumperOptions options = new DumperOptions();
options.setIndent(2);
options.setPrettyFlow(true);
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(options);

Here, we have specified the indentation and YAML document flow using the DumperOptions object. Now when we use the dump function on the Yaml instance we will get differently formatted output:

!!model.Student
address: Night City
courses:
- credits: 5.0
  name: Intelligence
- credits: 2.0
  name: Crafting
department: Cyberware
id: 21
name: Tim
year: 2077

Conclusion

As YAML files are becoming more frequent in use, especially for specifying project properties and build and deployment meta-data, it's increasingly useful to be able to handle them using code.

Through SnakeYAML, we can easily manage YAML files in our Java project, and a minimal amount of code is used to either load YAML files into our project or write data into YAML files. SnakeYAML also provides formatting options so you can tweak and customize to your needs.

The source code for the sample code can be found on GitHub.

Last Updated: February 27th, 2023
Was this article helpful?

© 2013-2024 Stack Abuse. All rights reserved.

AboutDisclosurePrivacyTerms