What is: YAML — общий обзор, типы данных, YAML vs JSON и PyYAML

Автор: | 03/14/2019
 

YAML — один из наиболее популярных форматов…

Они сами не знают — форматом чего, на самом деле.

Изначально он был «Yet Another Markup Language» — «Ещё один язык разметки», позже стал «YAML Ain’t Markup Language» — «YAML — не язык разметки» ((с) Wiki Rus и Wiki Eng):

Originally YAML was said to mean Yet Another Markup Language,[12] referencing its purpose as a markup languagewith the yet another construct, but it was then repurposed as YAML Ain’t Markup Language, a recursive acronym, to distinguish its purpose as data-oriented, rather than document markup.

В той Википедии даже слово «дружественный»  взято в кавычки, с чем лично я совершенно солидарен.

Вообще — просто ещё один формат сериализации данных, наследник JSON с дополнительными возможностями.

В недавнем опросе в Ukrainian DevOps Community YAML vs JSON — YAML набрал процентов 90% голосов, так что в самом деле на данный момент является наиболее популярным.

Лично для меня JSON был и остаётся наиболее предпочтительным форматом, но YAML приходится использовать.

Собственно, в этом посте — краткий обзор его основных типов данных и сравнение форматирования с JSON.

Основные принципы работы с YAML

  • всегда используйте UTF-8 во избежание ошибок
  • никогда не используйте TAB для отступов

Валидация YAML

Для проверки синтаксиса YAML в Linux можно использовать yamllint.

Устанавливаем:

sudo pacman -S yamllint

Проверяем:

yamllint monitoring.yml
monitoring.yml
1:1       warning  missing document start "---"  (document-start)
20:34     error    trailing spaces  (trailing-spaces)
22:32     error    trailing spaces  (trailing-spaces)
23:37     error    trailing spaces  (trailing-spaces)
33:7      error    wrong indentation: expected 8 but found 6  (indentation)
35:9      error    wrong indentation: expected 10 but found 8  (indentation)
36:11     error    wrong indentation: expected 12 but found 10  (indentation)

Правда при этом сам файл парсится Ansibl-ом нормально — но ошибки в форматировании есть.

Валидация JSON

И для примера — валидация JSON-документов из консоли Linux, используя модуль json:

python -m json.tool < json-example.json
{
"key1": "value1",
}

vim плагин

Имеется плагин vim-yaml.

Добавляем в .vimrc:

...
" https://vimawesome.com/plugin/vim-yaml-all-too-well
Plug 'avakhov/vim-yaml'

" add yaml stuffs
au! BufNewFile,BufReadPost *.{yaml,yml} set filetype=yaml foldmethod=indent
autocmd FileType yaml setlocal ts=2 sts=2 sw=2 expandtab
...

Перечитываем конфиг, устанавливаем плагин:

:source %
:PlugInstall

PyYAML

Для работы с YAML в Python имеется библиотека PyYAML.

Примеры работы с ней — ниже.

Форматирование в YAML

Комментарии в YAML

Одно из немногих преимуществ — возможность добавления комментариев.

Формат комментирования стандартен — использется #.

Добавление комментария допускается в любом месте строки.

Примеры:

---
# I'm comment
- name: somestring
  value1: "# I'm not a comment!"
  value: anotherstring  # another comment

Отступы

Основная головная боль YAML — отступы.

При этом во всём файле количество пробелов (не табуляций — только пробелы) в начале строк должно быть одинаковым.

Т.е. если для разделения элементов в одном месте используются два пробела — то во всём файле должны использоваться только два пробела, и никак иначе.

Более того — принято использовать 2 пробела, хотя допустимо любое, главное — одинаковое везде и во всём.

Например:

---
parent_key:
    key1: "value1"
    key2: "value2"
    key3: "%value3"

Будет являться валидным форматом, а:

---
parent_key1:
    key1: "value1"
    key2: "value2"
    key3: "%value3"

parent_key2:
  key1: "value1"
  key2: "value2"
  key3: "%value3"

Уже нет.

Тогда как в Python, который часто любят ругать за жёсткую привязку к пробелам — такое форматирование допустимо (хоть и будет нарушением стандарта):

#!/usr/bin/env python

def a():
    print("A")
    
def b():
  print("B")
  
a()
b()

И результат:

python spaces.py
A
B

Однострочный YAML

Кроме стандартного вида и разделения пробелами — можно использовать запись в одну строку, аналогично JSON, например:

---
parent_key: {key1: "value1", key2: "value2"}

Literal Block Scalar

YAML поддерживает возможность записи многострочных строковых блочных скаляров и имеет три варианта их записи — обычный, с помощью разделителя «|» и «>«.

Обычный формат будет выглядеть так:

---
string: This
    is
    some text
    without newlines

Результат в консоли Python:

>>> yaml.load(open('yaml-example.yml'))
{'string': 'This is some text without newlines'}

При использовании символа | (Literal style) — в значении будут сохранены все символы новой строки и замыкающие пробелы:

---
string: |
    This
    is
    some text
    with newlines

Результат:

>>> yaml.load(open('yaml-example.yml'))
{'string': 'This\nis\nsome text\nwith newlines\n'}

И с помощью > (Folded style):

---
string: >
    This
    is
    some text
    without newlines

Что вернёт весь текст одной строкой + замыкающий символ новой строки:

>>> yaml.load(open('yaml-example.yml'))
{'string': 'This is some text without newlines\n'}

При этом — вам всё-равно придётся поддерживать равное кол-во пробелов перед каждой строкой в самом YAML-файле.

См. шикарный ответ на StackOverflow тут>>>:

There are 5 6 NINE (or 63*, depending how you count) different ways to write multi-line strings in YAML.

Базовые форматы данных в YAML

В YAML используются три основных формата:

  • scalars: простейший типа ключ:значение
  • списки или последовательности (list/sequence): упорядоченные по индексам данные
  • словари (dictionary/mapping): схожи со скалярами, но могут иметь вложенные данные в т.ч. других типов

Scalars

Самый простой тип — скаляры, представляющие собой пару ключ:значение:

---
key1: "value1"
key2: "value2"

Использование кавычек для строковых данных категорически рекомендуется во избежание проблем со специальными символами:

cat example.yml
---
key1: "value1"
key2: "value2"
key3: %value3

Проверяем:

yamllint example.yml
example.yml
4:7       error    syntax error: found character '%' that cannot start any token

При этом значения типа true/false и integer можно смело указывать без кавычек.

Scalars — YAML vs JSON

Для сравнения — ключ:значение в YAML:

---
key: "value"

JSON:

{
    "key": "value"
}
Python

Пример работы с YAML-скалярами в Python:

>>> import yaml
>>> yaml.load("""
... key: "value"
... """)
{'key': 'value'}

Или из файла:

>>> import yaml
>>> yaml.load(open('yaml-example.yml'))
{'key': 'value'}

Списки в YAML

Списки (последовательности, lists, sequences, collections) представляют собой коллекции упорядоченных данных, доступ к которым возможен по их индексам.

Пример списка:

# SIMPLE LIST
- element1
- element2
Вложенные списки

Аналогично примерам выше — списки могут иметь вложенные списки, например:

# SIMPLE LIST
- element1
- element2

# nested list
-
  - element1

Именованные списки:

---
itemname:
  - valuename

При этом списки могут содержать скаляры или словари (про словари позже):

---
itemname:
  - valuename
  - scalar: "value"
  - dict: {item1: "value1", item2: "value2"}
Lists — YAML vs JSON

Список в YAML:

---
- item1
- item2
- item3

Список в JSON:

[
    "item1",
    "item2",
    "item3"
]

Вложенный список в YAML:

---
- item1
- item2
- item3
-
  - nested1

Вложенный список в JSON:

[
    "item1",
    "item2",
    "item3",
    [
        "nested1"
    ]
]
Python и YAML-списки

Тут всё аналогично:

>>> yaml.load(open('yaml-example.yml'))
['item1', 'item2', 'item3', ['nested1']]
>>> for i in yaml.load(open('yaml-example.yml')):
...   print(i)
...
item1
item2
item3
['nested1']

Словари

Словари, они же dictionaries, они же mappings, схожи со калярами и содержат пары ключ:значение и, в отличии от скаляров, которые являются элементарным типом — могут содержать вложенные элементы:

---
key1: "value1"
key2:
  - value2
  - value3

Или вложенные словари:

---
key1: "value1"
key2:
  - value2
  - value3
    
key3:
  key4: "value4"
  key5: "value5"
  key6:
    key7: "value7"
Dictionary — JSON vs YAML

Словарь в YAML:

---
key1: "value1"
key2:
  - value2
  - value3

И он же в JSON:

{
    "key1": "value1",
    "key2": [
        "value2",
        "value3"
    ]
}
Python
>>> yaml.load(open('yaml-example.yml'))
{'key1': 'value1', 'key2': ['value2', 'value3']}
>>> type(yaml.load(open('yaml-example.yml')))
<class 'dict'>

И, конечно, доступны все стандартные операции со словарями:

>>> dict = yaml.load(open('yaml-example.yml'))
>>> type(dict)
<class 'dict'>
>>> dict.update({'key3':'value3'})
>>> print(dict)
{'key1': 'value1', 'key2': ['value2', 'value3'], 'key3': 'value3'}

В целом — на этом всё, хотя у YAML много других возможностей.

Смотрите: