Для чего используется YAML

Еще один текстовый формат данных — YAML (Yet Another Markup Language, позже — YAML Ain't Markup Language). Используется для сериализации объектов для их передачи по сети (так же, как используется xml и json). За счет удобной читаемости его также используют для написания конфигурационных файлов, например, для Docker, Kubernetes, Ansible, etc. Если 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("Marina", 25) ,     
        new Person("Oleh", 73)      );
people:
  - name: "Ivan"
    age: 21
  - name: "Marina"
    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
---