Использование конфигурационных файлов в Go: YAML

В продолжение темы про конфигурационные файлы, взглянем на более распространённый формат YAML.

YAML (YAML Ain’t Markup Language) – это надмножество над JSON с упрощенным синтаксисом.

Синтаксис

Основные типы

Аналогично TOML, про который я писал ранее, в YAML можно использовать различные типы данных:

# Комментарии начинаются со знака "#"

# Пары ключ-значение разделяются через ":"
# Строки, как значения, могут быть заключены в кавычки (одинарные, двойные), 
# но не обязаны
key: значение
another_key: еще одно значение

# Числа можно использовать в экспоненциальной форме
a_number_value: 100
scientific_notation: 1e+12

# Логические значения требуется записывать как true или false.
# 1 и 0 будут считаться в этом контексте как строки.
boolean: true

# Значение может быть null.
null_value: null

# Ключ может содержать пробелы.
key with spaces: value

# Многострочные строки могут быть записаны двумя способами: 
# 'literal block' (используя символ '|'),
# или 'folded block' (используя символ '>').
literal_block: |
    Весь этот блок текста - значение 'literal_block'. 
    Переносы строк сохранятся.

    Литерал считается значением текущего ключа до тех пор, 
    пока отступ слева не будет уменьшен до уровня ключа. 
    Ведущие пробелы в итоговой строке будут опущены.

        Все строки, отступ у которых больше обычного (как в этой, например), 
        сохранят отступы. У этой строки отступ будет равен четырём.
folded_style: >
    Этот блок текста - значение 'folded_style'. 
    Но здесь переносы строк будут заменены пробелами.
    Пустые строки, как предыдущая строка, заменяются переносами строк.

        Строки с бОльшим отступом сохраняют переносы строк как в литералах - 
        этот текст будет располагаться на двух строках.

Коллекции

# Вложенность достигается за счёт отступов.
# Этот фрагмент эквивалентен следующему JSON:
# { "a_nested_map: { 
#       "key": "value",
#       "another_key: "Another Value", 
#       "another_nested_map: {
#           "hello": "hello"
#       }
#   } 
# }
a_nested_map:
    key: value
    another_key: Another Value
    another_nested_map:
        hello: hello

# Ключом словаря может быть не только строка:
0.25: a float key

# Ключ может быть сложным. Например, многострочными.
# Для этого необходимо использовать символ "?" с последующим пробелом 
# для обозначения начала сложного ключа:
? |
    Многострочный
    ключ из двух строк
: значение, соответствующее сложному ключу

# YAML позволяет создавать словари с составным ключом и значением-перечислением.
# Однако, не все парсеры поддерживают данный синтаксис.
# Пример:
? - Manchester United
  - Real Madrid
: [ 2001-01-01, 2002-02-02 ]
# Эквивалентный JSON: 
#  'Manchester United,Real Madrid': 
# [ Mon Jan 01 2001 04:00:00 GMT+0400 (Russia TZ 3 Standard Time),
# Sat Feb 02 2002 04:00:00 GMT+0400 (Russia TZ 3 Standard Time) ] }

# Массивы или списки:
a_sequence:
    - Item 1
    - Item 2
    - 0.5 # Последовательность может содержать различные типы данных.
    - Item 4
    - key: value
      another_key: another_value
    -
        - This is a sequence # Последовательность можно содержать подпоследовательности
        - inside another sequence

# Так как YAML - это надмножество JSON, 
# то можно использовать синтаксис JSON для словарей и массивов:
json_map: {"key": "value"}
json_seq: [3, 2, 1, "takeoff"]

Дополнительные возможности

# YAML поддерживает возможности использования anchor-ов. 
# Anchor похож на переменную: оба ключа в примере ниже будут иметь одно значение:
anchored_content: &anchor_name Эта строка будет значением для двух ключей.
other_anchor: *anchor_name

# Anchor можно использовать для дублирования/наследования свойств:
base: &base
    name: Все имеют одинаковые имена

foo: &foo
    <<: *base
    age: 10

bar: &bar
    <<: *base
    age: 20

# foo и bar будут иметь ключ "name" со значением "Все имеют одинаковые имена"

# YAML позволяет использовать теги, которые позволяют явно указывать типы:
explicit_string: !!str 0.5

# Некоторые парсеры имеют собственные теги.
# Например, тег в Python для комплексных чисел:
python_complex_number: !!python/complex 1+2j

Дополнительные типы данных

# В YAML можно использовать даты в ISO-формате:
datetime: 2001-12-15T02:59:43.1Z
datetime_with_spaces: 2001-12-14 21:59:43.10 -5
date: 2002-12-14

# Тег !!binary говорит о том, что следующая за ним строка - 
# это закодированные в base64 бинарные данные.
gif_file: !!binary |
    R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
    OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
    +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC
    AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=

# YAML содержит тип set:
set:
    ? item1
    ? item2
    ? item3

# Аналогично Python, set - это словарь, в котором значения всех ключей - null:
set2:
    item1: null
    item2: null
    item3: null

Работа с YAML в Go

Все возможности YAML поддерживает только один парсер: go-yaml.

API

Простейший пример выглядит элементарно:

package main

import (
       "gopkg.in/yaml.v2"
       "log"
       "fmt"
       "time"
)

var data = `
a: Easy!
b:
  c: 2
  d: [3, 4]
m: [ "2001-01-01T15:04:05Z", "2002-02-02T15:04:05Z" ]

`

type T struct {
       A string
       B struct {
              RenamedC int   `yaml:"c"`
              D        []int `yaml:",flow"`
       }
       M []time.Time
}

func main() {
       t := T{}

       err := yaml.Unmarshal([]byte(data), &t)
       if err != nil {
              log.Fatalf("error: %v", err)
       }
       fmt.Printf("--- t:\n%v\n\n", t)
}

Из коробки есть возможность распарисить строку в структуру (этим уже не удивишь).

Документация утверждает, что библиотека поддерживает anchor-ы и теги. Проверим на практике:

type T1 struct {
    A string `yaml:"a"`
    B string `yaml:"b"`
    I string `yaml:"i"`
    Base struct{
        Name string
    }
    Child struct {
        Name string
        Address string
    }
}

func anchorsAndTags() {
 var data = `
a: &x Simple Value
b: *x
i: !!binary iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAASnUlEQVR42u2dfYhdZX6AnzMMwzAMIYQQ0hBCCKObRt11tet33VRjtK1NxY+67vGjdte1aUhtCKlYEWnF6q674kd13XRr3a1jbatu2JZFUllKsFZERELIWhtSkSEMaQjDMIRhCJn+cd6r10lmkrlz77nnfc/zQHDVdT7ee37P73fej98LIiIiIiIiIiIiIiKSJplDUC+mh+kF1gLnAkvDPx4D9gP7spwpR0kBSHqBvwTYAdwJrJjl/3YE2AU8m+XsddQUgKQR/GuA14Hzz/A/mQJ2Ag9mOeOOoAKQuDP/vwKXtfCf7wZuy3KOOpJp0uMQJP++/3CLwQ+wEXhsetjnRAFIjGwEvr3Ar/EN4CKHUgFIXNl/KfAIMLDAL7UI2D49TJ+jqgAkjuAnZP4L2vQlfwe4wpFVABIHQ8CWNn69gVAF9Du0CkCqnf17gG3AyjZ/6ausAhSAVJ/zKSbu2k0/cJ9VgAKQ6mb/XmA7sKRD32JjqAREAUgFuQzY1MGv3xeqgAGHWgFItbJ/f3j3H+zwt1of/ogCkApxJXBdCd+nj2JFYNAhVwBSjew/QHHSr6wJuiuBDY68ApBqsD4EZVn0hrkAqwAFIBXI/ttCaV4mV1gFKADpPhtLzv5WAQpAKpL9B4H7upD9rQIUgFSADXR3e24vsNV9AQpAupf9e7v8o1yB+wIUgNQu+zdwd6ACkJpm/wbrrQIUgNQv+1sFKACpefa3ClAAUuPsbxWgAKTm2d8qQAFIjbO/VYACkA5n/wFgc4Wzv1WAAhADyypAAUgnsn839/xbBSgAMaCsAhSAmP2tAhSAGEhWAQpAzP6nk5e3CSkAaZHYj9r2Yb8ABSAtZ/+tEWf/BhsoLiwRBSDz4DLSaLfV71yAApD5Zf/+kP1TuYjTKkAByDy4hLSabfYDm71ZWAHImWf/1Erm64CL/IQVgMzNRZRzx1/ZDIS5AKsABSBzZP/7Esz+VgEKQGqc/a0CFIDUPPs32Aic7yeuAOSLXBCCI3UGQxXQ50euAKTI/n0UM/91uWjzeqsABSBfzP7X1+j3HaTYF9DrR68A6p79eyl6/dXtmu0brAIUgBRBsKmGv/diYItVgAKoe/bfEoKhjlgFKIDaZ/8bavz7WwUoALN/zYfCKkABmP2tAqwCFIDZ3ypAFIDZ3ypAFIDZv15sAs51GBSA2b+eLMHdgQog8ezfZ/afk5utAhRAynzZ7H/aKmCrVYACMPvXlxtxRUABmP1riysCCsDsX3PcF6AAksv+NzoM86oCttk1SAGkkv23AYscjXmxCfgNh0EBxM4l1PO8/0Jp9A60g7ACiDb7DwDbqV+3n3ZxfRCoKIAouYx6dPrtFAPAdm8WVgAxZv9BYAdYwi6QDcBVDoMCiCn4objhxwd34fQD908Pu4SqAOJhacj+bmZp36vUjUGsogAqnf17gG/iEla7n9P7gdUOhQKoOutC9nds28vZwAMuC7aXzCFY0Ht+P8US3yCwguIo6z1m/44xBewE/h04CBwFJoBjWc5xh0cBdCLQeymWoxqBPhSy0a+HknRlCH4nqcrlOHAIGAVGgI+BX4W/jjbEEOQgCuC07+2NbL4oBPRa4EvAmvD3y4FluKwXA2Mz5PDfwIEgh0bVMJHlTCmA+pbsy0I2HwqB3sjkK0LGl/Q4ARwJcjgUXiMacvgEGA9Vw0RdXimyxIN8SSjX1wBnNQX58vDvRJrnF0ab/hwE/if8NVk5ZJEGel9TkC9uei8/C1jVFOSLcTZe2iuHmZXDSJBDYzLyhAJof5AvCll8qCnIl4dAX2aQSxc51iSGQ02Vw4Hw9xNVlkPW5SDvnSPIVzcF+VLcVSfxMTFH5TA6o3JIUwBhhn2w6c+qpnJ9zYxMbgcYqQvjTWJorFQcDHI4zOcrFZNRCCDs0Gpk8uV8vlbeWEZrBLkz7CKzc4JiqfJwkMOnQQ4fBzmMNSqHdrxSZPMM8p6mIF8cyvTGevmqpkB3hl2k/TRPRo40vVJ81PRKMT6fqiE7TcAvD9l8XcjmZ/PFWXZLdpFq0Nj81Jhr+BWwP/zvkdmkkM0R+N8GcopJOSfgROKda3gbeA54a+bux+wUJf6VwLN4R5tIShwHXgQeynIOnySAEPx3Ak9jC2uRVHkXuCXLGYEvbqD5g1AmGPwi6XIJ8Pr0MEs/qwCmhxkC/otiw42IpM+LwD1ZOEDzM7y4UqRucwJf76FY0rve8RCpFb3A+h6Kiytd5hOpoQR6KLbnikgN6cFediK1FsAxh0GkvgIYdRhE6iuA/RBXGyMRaQsneihOC+13LERqx7s9oRXR0+DNKiI14kNgT+MswEvAC46JSC0YBe7KcqaaTwP2A38PfMPxEUmWceCmLOctaDoNGDqG3AP83DESSZJjwOZG8H9BAEECE8DdwJuOlUhSTFFcW/9K8z886UKNLOcocBfwS8dMJJngfwh4Yeb9A6e8USe0DMopuoeISLwcBx4Fvn+qNuKzXqmV5YwCtygBkaiD/3Hgr2e7Q+C09wJMD7MSeB24yPEUiS74/3Ku24zP6GKQ6WFWU3QNOt9xFUkj+Od8BZjxOvAJcBPF7iERSSD4z7gCaKoE1gD/AlzgOItUjqkQ/I+cSfDPWwBBAquAf6JoLywi1Qn+h4AnzzT4WxJAkMDKUAkoAZHqBP/353tjcE8r3y3cKnIL8I5jLxJn8LcsgCYJ3Aqf7ysWkVKZXEjwt/wKMON1YBkwDGzw8xApjWMUe/tfaDX42yIAJSBSOhMh+HcuJPjbJoAggeXAT4CNfj4iHQ3+LcDLCw3+Bc0BnGJOYJTiAJH9BEQ6w1g7g7+tFUBTJbCEorPQJj8vkbYG/7eAN2Ye6a1EBdBUCRylaCryqp+ZSFs42ong70gF0FQJLAaeA77p5yfSModDQv1Fu4O/owJoksDTwO2dqDZEahD8dwC7OxH8dDoos/yzSYsf4+1DIpUK/o4LIEhgAtiuBETOmNEygr/jrwAzXgcGgMeAPwF6/YxFTskIxXL6nk4HfykVQFMlcAy4H3gGryET6Xrwl1oBNFUC/RRdSv/USkDkMw6G4H+3rODvigCaJPAA8BdKQISDFCdr3y8z+LsmgCCBPuBBJSA15wBwWzeCv6sCaJLAnwcR9PssSM3YHzL/vm4Ef9cFECTQC/wZ8IgSEIO/XLq+Oy80MHyKorPJpM+F1IAPKFrqdTX4K1EBzKgEvgM8AQz4jEjCwX9rlnOgCj9MVqWRmR6mJ0jgB0pAEuQ9IK9K8FdOAE0S+KMggUU+M5IIb4fg/7RKP1RWxZEKErgdeFYJiMHfOSp5RDe0O3oZ2AqM+/xIxPyyqsFf2QpgRiVwM/BDYInPkkTGbuDuLOdQVX/ArOojGCRwA/C3SkAM/poJQAlIpMF/V+iUjQJQAmLwV5Jo+vSFicFdwD3AEZ8zqSC7Ygr+qCqAGZXAdRR3DyzzmZOK8AZwb5bHlZyyGEc6SGAjxVVkSkC6zWvA5tiCP6pXgFO8Duym6KAy4vMnXeTlGDN/1BVAUyUAcCXFzcQrfRalRE4ArwBbQ/v7KIn6so5wlHKPlYB0Ifh3xh780VcAp6gEfgKs9vmUEoJ/R7jzImqSuK6rqRK4Fapz1FKSo9G8JongT6YCmFEJXBTmBIZ8XqUDwf9wuOMCBaAEpB5MAd8DHksp+JMUQJMELgD+AVjn8ysLDP5Hge9leXo9K7NUP7UggS8D/6gEpEUmKbpVP5li8CctACUgbQj+B4FnQufqJOlJ+RMMqwN7KW5e+cRnWubBo6kHf/ICaJLAAahmSyapLHtTD/5aCCAwgBuEZH7U4pWxLgJYgmcFZH6cE06dKoAEWFuj31XawxA1uJymLkHhCoDMlzUKIAFCGXeOz7PMk6XAcgUQPwO4JVhai411CiANAazxeZYWOE8BpFHKLfVZlhZYG66tVwARcy6uAEhrJD8RWIfAOM/nWBRADQUQyre1PsfSIouAVQogXpwAlHa8QiqASOnHMwCywFfIcKxcAUTISrxIVBbGENCnACzfpJ4kPRGYugDcAiztEMCgAoiM6WH6cAVAFk4/CW8l70n8g3MFQHyVrKkABnEFQNr0KpnqSkDKAlid8rublMpQqCgVQETYBETaKYABBRAJoVz7is+ttImVwGIFEA9OAEo7SfZMScoCsAuQtJNzFUA8DJL4KS4pnSTbhKcqgLNJdNZWukaSKwGpCsAVAGk3SZ4JSE4AYQXALkDSbpaRYG/JFCsAJwClU7FyrgKoPnYBEl8tayyARXgRqHSGc1JrE56iANZC2r3cpWsktxKQogDsAiSdIrmVgKQE4EWg0mEWp/Z6mVoF4BkAscKsuQBcApROklSb8NQEUIs73aWrnE1CbcJTE4AXgUqnWUNCKwGpBYtnAKQMAQwogIoRNmi4AiCdZoCE5plSqgCcAJQyXzUVQAUF4BKglEEyKwEpCcCLQKUskmk4k5IAnACUslitAKqHE4BSFqtI5NKZJAQQVgDO9rmUkuhL5XlLpQJwBUB85ayxAOwCJGVzXgptwlMRwGqKTkAiZTFEAmcCUhGATUBaYwI4CJxwKFoSQL8C6DK2AW+JcWAncDlwIXAH8K4imBdJ7DtJoQLoI9GLGzuU8V8ELgW2ZDl7s5wx4BXgGuBW4D1FcMaxs04BdJ/+MAcgszMJvBQC/94sZ3+Wc7zxL7McspwJ4DXg6lAR7HXYTkv0AsgSeAVYAfwvCTVpaHPg7wKeAPY2B/0ZvFYtAm4GtuEcy2z8FLg7y+OtmFKoAIYM/pOYAn4O/BZwR5bzwZkGf1NFMB5eFy4HNgMfO6wnEX1zkBQE4Pv/5xwH3gKuBW7Jct6dT+DPIYIXggi2AZ86zAqgKuU/uAIAxaTdHuD3gN/Ncv4jy5lq23tiIYIjwFPA14AHgVGHneVEvhIQewXQR723AJ+gmLW/Bbg2y3mznYE/iwgOA49TLB8+Dhyp8fj3xF6Bxi6AOq8AfEgxW3818EaWM1nWN85yTmQ5h0IlcDHwDMXegjoS9UpA1KsANV0B+IhiVv+fgYksr8Tn0BMqse3A7SR2fdZpiHolIPYKoE4rAAeBLRRr+S9meTWCv6ki+JhiteDiEBSTNflcop4IjF0AdVgBGAF2UEy+PZ/ljFUl8GcRwT7gWxSrBm9A5+YkKiSAaJNQtNdohxWAlLsAHQGeB34IjFY16GcRwXHgg+lhbgUuCXMFG0jz2vblFDdSjSmAcukjzR4AY8CPgaeBQzHvMgsieHt6mN8H1gcRXEFareh6KLoDHfAVoFxS6wI0QbHh5kLg/ixnJObgnyGCqSxnN8WBo5uA90nrwFG0KwGxC2BVAg/PJMWkWeOE3sFUAn8WEewCvk6xhLkvkV/tS7F2B4pZAKuJe7lpiuKgzuUUy0j7Ug38U4jgGMUR5EuBe2Mtn5uIdiIwZgHE2pW1sV//Gor9+h/UJfBnSKBxBHknxQrHduBQpL/OKgXQhbIrsp/3BPAOxX79385y9izkoE5iIhgDngS+CvwVcDRCAQwogJII9wDEtALwIUW3navDfv3aB/4sIjgMPAx8heLgUSzbi/uIdEt6rBVALAP+EXA3cGmW81qZ+/UjF8FIeCW4kKInQQzjNqQAyhXAygr/fJ9SbNu9GHjJwG9JBCeynAPAPWEcX6XauwrPUgDl0U+xA6tqjAIPhBL2+SxnPKYdfBUWwV4gB34T+Deo5CvUqhiXAmPdCbiSas26jgF/AzwLHDboOyMC4L2wq/CKMFewvkJJrPFMTiqAEmxbkZ9jnKLb7hPAiIFfmgj2TA9zLcX5gocozht0WwQrYoynWAWwosvf/xjwMvBd4KCB3xURHAfenB7mLWATxTmD87sogmVEuBcg1jmAX+vS92301/8qRX99g78CIshy3qDYVZgD+7v0oyyJMaHGKoD/K/n7TVHMQn+NYtvuxwZ+5UQwleW8SrF0eDflby8eg/j2d8QqgPe6EPh52K8v1RbBZJZ/VqVtpuikVAb7wquhAiiB/RSdcjrFcT4/qHNbuEPP+/LiEsFElvNCEMG2Dj8vUGzznlQA5ZVbP+1Q4L8ZAv+mLOd9M370Ihin2FZ8DnA/nbnP4BjwdzE+K9F2BZ4eZhXwn7RnR2DjhN6jwDtm+zQJbeQWA38cqoJlbfrSTwE7YjzjkUX+YV4P/IzWZ18bGf+7wNtm+1rJoF0ieA+4JlQaVgAlf4g9FH3of8T8WjMfB34BPAKW+YqgZRG8H14Vo70vMYXrwQGuAp7j9G3CDXyZSwTfAe7j9BvNpig2gu3I8uh6F6QlgKYPcAD4Q4rNIDO3hh4DXgN+EA6WiMz1HN0JbOXkZp+HKFaHfgTsNYGIiIiIiIiIiIiIiEh1+X+xQV6CEd39pgAAAABJRU5ErkJggg==
base: &base
 name: Одно имя на всех
child:
 <

В общем-то, всё работает как надо:

  • после выполнения этой программы в корне проекта появляется файл img.png с закодированным изображением,
  • значения свойств A и B структуры – совпадают и равны Simple Value,
  • свойства Base.Name равно свойству Child.Name, а свойство Child.Address равно Мой адрес не дом и не улица.

Скорость на больших конфигурационных файлах

Для анализа возьму файл, который использовал в примерах для TOML. Размер полученного файла – примерно 1Мб. Не bigdata, но сейчас этого будет достаточно.

Для профилирования использую библиотеку stopwatch, которую так же использовал в предыдущий раз для профилирования парсинга TOML.

sw := stopwatch.New()
sw.Start(0)
singleYaml() 
log.Printf("Yaml simple decode: %s", sw.ElapsedTime().String())

sw.Reset()
sw.Start(0)
for i := 0; i < 10; i++ {
       largeYaml() 
}
log.Printf("Large yaml 10 times: %s", sw.ElapsedTime().String())

Замерил скорость работы для маленького и для большого YAML-файлов. Результаты получились очень привлекательные: 3.0017ms для первого и 186.0448ms для второго случаев. Для сравнения, при разборе TOML лучший результат для аналогичного файла был 2.49 секунды.

Итого

YAML – более распространённый на данный момент формат конфигурационных файлов. Возможно, это, в том числе, является причиной более производительной работы.

Так же радуют интересные фичи YAML, anchor-ы и теги, позволяющие удобнее организовывать данные.

Все примеры можно найти в репозитории на bitbucket.

Ссылки

  1. http://yaml.org/
  2. http://docs.ansible.com/ansible/YAMLSyntax.html
  3. https://learnxinyminutes.com/docs/yaml/
  4. http://nodeca.github.io/js-yaml/
  5. https://github.com/Animosity/CraftIRC/wiki/Complete-idiot’s-introduction-to-yaml
Advertisements

One thought on “Использование конфигурационных файлов в Go: YAML

  1. Pingback: Использование конфигурационных файлов в Go: INI | i can do some code for you

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s