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.
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.