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

Автор: | 14/03/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.

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

[simterm]

$ sudo pacman -S yamllint

[/simterm]

Проверяем:

[simterm]

$ 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)

[/simterm]

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

Валидация JSON

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

[simterm]

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

[/simterm]

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

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

[simterm]

:source %
:PlugInstall

[/simterm]

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()

И результат:

[simterm]

$ python spaces.py 
A
B

[/simterm]

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

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

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

Literal Block Scalar

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

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

---
string: This
    is
    some text
    without newlines

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

[simterm]

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

[/simterm]

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

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

Результат:

[simterm]

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

[/simterm]

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

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

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

[simterm]

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

[/simterm]

При этом – вам всё-равно придётся поддерживать равное кол-во пробелов перед каждой строкой в самом 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"

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

[simterm]

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

[/simterm]

Проверяем:

[simterm]

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

[/simterm]

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

Scalars – YAML vs JSON

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

---
key: "value"

JSON:

{
    "key": "value"
}
Python

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

[simterm]

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

[/simterm]

Или из файла:

[simterm]

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

[/simterm]


Списки в 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-списки

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

[simterm]

>>> 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']

[/simterm]


Словари

Словари, они же 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

[simterm]

>>> yaml.load(open('yaml-example.yml'))
{'key1': 'value1', 'key2': ['value2', 'value3']}

>>> type(yaml.load(open('yaml-example.yml')))
<class 'dict'>

[/simterm]

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

[simterm]

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

[/simterm]


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

Смотрите: