Lectura y escritura de archivos YAML en Java con Jackson

YAML es un lenguaje de serialización de datos simple y legible por humanos, muy parecido a JSON. Usando Jackson, una popular biblioteca de Java presente en muchos proyectos, podemos leer y escribir YAML fácilmente.

Introducción

Los archivos YAML se utilizan actualmente de forma generalizada para definir propiedades de herramientas y aplicaciones debido a su sintaxis muy legible por humanos.

Además de contener propiedades de configuración, también se usan a menudo para la transmisión/serialización de datos, de forma similar a como se usa JSON.

Leer y escribir archivos YAML se está convirtiendo rápidamente en una habilidad básica del desarrollador, similar a como lo es leer y escribir archivos JSON y XML.

¿Qué es YAML?

YAML Ain't Markup Language (YAML) es un lenguaje de serialización de datos simple y legible por humanos, muy parecido a JSON. Es más fácil de leer y comprender y tiene la capacidad de hacer referencia a otros elementos dentro de un archivo YAML, así como también incrustar otros formatos como JSON y XML dentro de sí mismo.

No pretende ser un reemplazo de las soluciones actuales y se usa principalmente para archivos de configuración debido a que es fácil de leer y editar, y admite comentarios, lo que lo hace mucho más amigable para el equipo.

La mayoría de los archivos YAML se pueden convertir a JSON y viceversa, ya que YAML es un superconjunto de JSON. Aquí hay un ejemplo de un archivo YAML:

1
2
3
4
5
6
7
8
--- #Employee Info
name: David
wage: 1500
position: Developer
techstack:
    - Java
    - Spring
    - Hibernate

La sintaxis es realmente simple, un diccionario (nuestra entidad empleado) se representa con <clave>: <valor>.

Después de definir algunas propiedades como nombre, salario y puesto, se incluye una lista, de nuevo, sangrada con cada elemento de la lista que comienza con -.

Cada uno de estos elementos también puede ser un diccionario:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
---
name: David
wage: 1500
position: Developer
colleagues:
    -   name: Martha
        wage: 1500
        position: Developer
    -   name: Jim
        wage: 1800
        position: DevOps Engineer

Puede acortar diccionarios y formar colecciones más complejas, aunque eso está fuera del alcance de este tutorial.

Con un repaso en YAML, estamos listos para escribir código que lea/escriba archivos YAML. Para lograr esto, podemos usar cualquiera de las dos bibliotecas populares: jackson o SerpienteYAML. En este artículo, nos centraremos en Jackson.

Leyendo YAML con Jackson

Jackson es una biblioteca basada en Java extremadamente popular que se utiliza para analizar y manipular JSON y [XML](/leer-y-escribir-xml-en- archivos java/).

No hace falta decir que también nos permite analizar y manipular archivos YAML de manera similar a como ya estamos acostumbrados a hacerlo con los dos formatos mencionados anteriormente.

Al usar Jackson como nuestra biblioteca, estamos en un entorno familiar y, de todos modos, muchas aplicaciones Java ya tienen Jackson para otros fines.

Primero, agreguemos a Jackson a nuestro proyecto a través de Maven:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-dataformat-yaml</artifactId>
    <version>{$version}</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>{$version}</version>
</dependency>

Necesitaremos tanto jackson-databind de donde extraeremos la clase ObjectMapper como la dependencia jackson-dataformat-yaml de donde extraeremos la clase YAMLFactory.

Al igual que hicimos con JSON y XML, la clase principal que usaremos para esta tarea será la clase ObjectMapper. Su método readValue() se usa para mapear la fuente (archivo YAML) en el resultado (un objeto de una clase).

Primero definamos una clase simple a la que nos gustaría mapear. Con respecto al ejemplo anterior de YAML, hagamos una clase Empleado:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Employee {

    public Employee(String name, int wage, String position, List<Employee> colleagues) {
        this.name = name;
        this.wage = wage;
        this.position = position;
        this.colleagues = colleagues;
    }

    // Without a default constructor, Jackson will throw an exception
    public Employee() {}

    private String name;
    private int wage;
    private String position;
    private List<Employee> colleagues;

    // Getters and setters

    @Override
    public String toString() {
        return "\nName: " + name + "\nWage: " + wage + "\nPosition: " + position + "\nColleagues: " + colleagues + "\n";
    }

Ahora, leeremos un archivo YAML, digamos person.yaml, con el contenido:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
---
name: David
wage: 1500
position: Developer
colleagues:
    -   name: Martha
        wage: 1500
        position: Developer
    -   name: Jim
        wage: 1800
        position: DevOps Engineer

Asignaremos este archivo a una instancia de nuestra clase Employee usando la clase ObjectMapper de Jackson:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Loading the YAML file from the /resources folder
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
File file = new File(classLoader.getResource("person.yaml").getFile());

// Instantiating a new ObjectMapper as a YAMLFactory
ObjectMapper om = new ObjectMapper(new YAMLFactory());

// Mapping the employee from the YAML file to the Employee class
Employee employee = om.readValue(file, Employee.class);

// Printing out the information
System.out.println("Employee info " + employee.toString());

// Access the first element of the list and print it as well
System.out.println("Accessing first element: " + employee.getColleagues().get(0).toString());

Ejecutar este fragmento de código nos dará:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Employee info
Name: David
Wage: 1500
Position: Developer
Colleagues: [
Name: Martha
Wage: 1500
Position: Developer
Colleagues: null
,
Name: Jim
Wage: 1800
Position: DevOps Engineer
Colleagues: null
]

Accessing first element:
Name: Martha
Wage: 1500
Position: Developer
Colleagues: null

Nuestro archivo person.yaml se asignó a una instancia de un objeto que luego podemos usar para cualquier propósito previsto.

Escribir YAML con Jackson

Con la lectura y el mapeo fuera del camino, sigamos adelante y escribamos en un archivo YAML.

A diferencia de usar el método readValue(), usaremos el método writeValue(), especificando dónde terminará el archivo resultante y el objeto desde el cual estamos mapeando en el archivo YAML.

Vamos a crear una instancia de un empleado y asignarle algunos valores:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
List<Employee> colleagues = new ArrayList<Employee>();

colleagues.add(new Employee("Mary", 1800, "Developer", null));
colleagues.add(new Employee("Jane", 1200, "Developer", null));
colleagues.add(new Employee("Tim", 1600, "Developer", null));
colleagues.add(new Employee("Vladimir", 1000, "Developer", null));

// We want to save this Employee in a YAML file
Employee employee = new Employee("David", 1500, "Developer", colleagues);

// ObjectMapper is instantiated just like before
ObjectMapper om = new ObjectMapper(new YAMLFactory());

// We write the `employee` into `person2.yaml`
om.writeValue(new File("/src/main/resources/person2.yaml"), employee);

Ejecutar este fragmento de código nos dará un archivo YAML con el contenido:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
---
name: "David"
wage: 1500
position: "Developer"
colleagues:
- name: "Mary"
  wage: 1800
  position: "Developer"
  colleagues: null
- name: "Jane"
  wage: 1200
  position: "Developer"
  colleagues: null
- name: "Tim"
  wage: 1600
  position: "Developer"
  colleagues: null
- name: "Vladimir"
  wage: 1000
  position: "Developer"
  colleagues: null

Como puede ver, el objeto employee de Java se serializó en un archivo con formato YAML utilizando el soporte de YAML de Jackson.

Conclusión

Los archivos con formato YAML son cada vez más populares para definir propiedades de herramientas y aplicaciones debido a la sintaxis muy legible por humanos. Además de contener propiedades de configuración, también se usan cada vez más para la transmisión de datos, de forma similar a como se usa JSON, aunque su uso en esta área todavía no es tan frecuente en la naturaleza.

Jackson es una biblioteca basada en Java extremadamente popular que se utiliza para analizar y manipular JSON y [XML](/leer-y-escribir-xml-en- archivos java/). Además, se amplió para permitir que los desarrolladores también trabajen con el formato de archivo YAML.