Для чого використовується YAML

Ще один текстовий формат даних — YAML (Yet Another Markup Language, пізніше — YAML Ain't Markup Language). Використовується для серіалізації об'єктів для їхньої передачі мережею (так само, як використовується xml та json). Завдяки тому, що його легко читати, він також використовується для написання конфігураційних файлів, наприклад, для Docker, Kubernetes, Ansible тощо. Якщо YAML потрібно зберегти до файлу (скажімо, конфігураційного), використовують два розширення: .yaml і .yml.

Синтаксис мови

Для позначення тегів у xml використовують кутові дужки (<>), для JSON — фігурні ({}). Для YAML використовують роздільники рядка (Enter) і відступи (пробіли чи таби).

Дані зберігаються у вигляді key-value пар, де ключем виступає рядок, а значеннями можуть бути різні типи даних: рядок, число, true/false, масив... Ключі пишуться без лапок.

Давай послідовно подивимося, як у YAML зберігається інформація:

Тип Java YAML
Ціле число

int number = 5
number: 5
Дробове число

double number = 4.3
number: 4.3
Булева змінна

boolean valid = false
valid: false
valid: no
valid: off

* Допустимі булеві значення: true/false, yes/no, on/off.

Рядок

String city = “Kyiv”
city: Kyiv
city: 'Kyiv'
city: "Kyiv"

* Усі три варіанти рівнозначні.

Рядок зі спецсимволами

String line = “aaa\nbbb”
line: "aaa\nbbb"
Коментар у коді

// comment
# comment
Об'єкт

public class Person {
  String name = “Vasyl”;
  int age = 32;
}

* Наведено клас об'єкта, щоб було видно структуру об'єкта.

person:
  name: "Vasyl"
  age: 32

* Зверни увагу на відступ перед атрибутами. Він має бути однаковим для всіх атрибутів.

Список простих значень

var ages = 
    List.of(1, 3, 5, 9, 78, -5);
ages: [1, 3,5,9,78, -5]
ages:
  - 1
  - 3
  - 5
  - 9
  - 78
  - -5

* Обидва варіанти є рівнозначними.
** Кожен наступний елемент списку позначається дефісом.

Список об'єктів

class Person {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
….
List<Person> people = List.of(
        new Person("Ivan", 21) ,     
        new Person("Maryna", 25) ,     
        new Person("Oleh", 73)      );
people:
  - name: "Ivan"
    age: 21
  - name: "Maryna"
    age: 25
  - name: "Oleh"
    age: 73

Як і в Java, елементом списку може бути список, тобто об'єкти можна вкладати один в інший. Дефіс, який визначає черговий елемент списку, може бути зміщений за горизонталю щодо батьківського ключа або знаходитися безпосередньо під ним. Головне – щоб усі елементи мали однаковий формат. Це допоможе уникнути плутанини та неоднозначності з ієрархією вкладеності.

ages:
  - 1
  - 3
  - 5
  - 9
  - 78
  - -5
ages:
- 1
- 3
- 5
- 9
- 78
- -5

Є ще два нюанси при роботі з текстовими значеннями:

  1. Багаторядковий текст. Ми можемо зберегти текст таким чином:

    multilineText: "line 1\nline 2\n....line n"

    Але читати його будет дуже незручно. Тому існує символ | (pipe), за допомогою якого можна записати текст інакше:

    multilineText: |
     line 1
     line 2
     ....
     line n

    Погодься, другий варіант зручніше, чи не так?

  2. Довгі рядки. Якщо текст потрібно зберегти в один рядок, але водночас хочеться, щоб він розміщувався у видимій робочій області IDE, можна використовувати символ > (greater).

    singlelineText: >
     begin
     ...
     continue same line
     ...
     end

    Весь текст буде сприйнято як один рядок.

Якщо необхідно декілька YAML-структур даних записати в один файл, тоді між ними потрібно використовувати роздільник (три дефіси). На практиці потреба зустрічається рідко, але добре знати про такий варіант.

Приклад YAML-документа

Давай створимо в Java якусь структуру даних (клас) та об'єкт цього типу – і спробуємо подати дані цього об'єкта у серіалізованому вигляді у форматі YAML.


class Family {
   private Date weddingDate;
   private Person wife;
   private Person husband;
   private List<Person> children;

   // getters and setters are omitted
}

class Person {
   private final String name;
   private final boolean isWoman;
   private int age;

   public Person(String name, int age, boolean isWoman) {
       this.name = name;
       this.age = age;
       this.isWoman = isWoman;
   }

// getters and setters are omitted

}

public static void main(String[] args) {
   Person wife = new Person("Anya", 37, true);
   Person husband = new Person("Alex", 40, false);
   var children = List.of(
           new Person("Inna", 12, true),
           new Person("Olya", 5, true)
   );
   Date weddingDate = new Date(/*some long*/);

   Family family = new Family();
   family.setWeddingDate(weddingDate);
   family.setWife(wife);
   family.setHusband(husband);
   family.setChildren(children);
}

Валідне подання в YAML:

---
weddingDate: 2000-12-03
wife:
 name: Anya
 age: 37
 isWoman: yes
husband:
 name: Alex
 age: 40
 isWoman: no
children:
 - name: Inna
   age: 12
   isWoman: true
 - name: Olya
   age: 5
   isWoman: true
---