Существует ли синтаксис YAML для совместного использования части списка или карты? - PullRequest
75 голосов
/ 13 февраля 2012

Итак, я знаю, что могу сделать что-то вроде этого:

sitelist: &sites
  - www.foo.com
  - www.bar.com

anotherlist: *sites

И имеют sitelist и anotherlist, оба содержат www.foo.com и www.bar.com. Тем не менее, я действительно хочу, чтобы anotherlist до также содержали www.baz.com, без необходимости повторять www.foo.com и www.baz.com.

При этом возникает синтаксическая ошибка в синтаксическом анализаторе YAML:

sitelist: &sites
  - www.foo.com
  - www.bar.com

anotherlist: *sites
  - www.baz.com

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

sitelist: &sites
  - www.foo.com
  - www.bar.com

anotherlist:
  - *sites
  - www.baz.com

Это означает, что пользователь этого файла YAML должен знать об этом.

Есть ли чистый YAML способ сделать что-то подобное? Или мне придется использовать некоторую обработку после YAML, такую ​​как внедрение подстановки переменных или автоподъем определенных типов подструктуры? Я уже делаю такую ​​постобработку, чтобы обработать пару других вариантов использования, поэтому я не совсем против этого. Но мои YAML-файлы будут написаны людьми, а не сгенерированы машиной, поэтому я хотел бы свести к минимуму количество правил, которые должны запомнить мои пользователи, в дополнение к стандартному синтаксису YAML.

Я также хотел бы сделать аналогичную вещь с картами:

namedsites: &sites
  Foo: www.foo.com
  Bar: www.bar.com

moresites: *sites
  Baz: www.baz.com

У меня был поиск по спецификации YAML , и я не смог ничего найти, поэтому я подозреваю, что ответ просто "нет, вы не можете этого сделать". Но если у кого-то есть идеи, это было бы здорово.


РЕДАКТИРОВАТЬ: Поскольку ответов не было, я предполагаю, что никто не заметил ничего, чего у меня не было в спецификации YAML, и что этого нельзя сделать на уровне YAML. Так что я открываю вопрос к идее для последующей обработки YAML, чтобы помочь с этим, на случай, если кто-нибудь найдет этот вопрос в будущем.

Ответы [ 5 ]

48 голосов
/ 02 марта 2012

Тип ключа объединения , вероятно, то, что вы хотите. Он использует специальный ключ сопоставления << для указания слияний, что позволяет использовать псевдоним сопоставления (или последовательность таких псевдонимов) в качестве инициализатора для объединения в одно сопоставление. Кроме того, вы все равно можете явно переопределить значения или добавить больше, которых не было в списке слияния.

Важно отметить, что он работает с отображениями, а не с последовательностями, как ваш первый пример. Это имеет смысл, когда вы думаете об этом, и ваш пример выглядит так, как будто он не должен быть последовательным в любом случае. Простое изменение значений последовательности на ключи сопоставления должно помочь, как в следующем (непроверенном) примере:

sitelist: &sites
  ? www.foo.com  # "www.foo.com" is the key, the value is null
  ? www.bar.com

anotherlist:
  << : *sites    # merge *sites into this mapping
  ? www.baz.com  # add extra stuff

Некоторые вещи, на которые стоит обратить внимание. Во-первых, поскольку << является ключом, его можно указывать только один раз для каждого узла. Во-вторых, при использовании последовательности в качестве значения порядок значим. Это не имеет значения в приведенном здесь примере, поскольку нет связанных значений, но об этом стоит знать.

15 голосов
/ 18 января 2017

Как указывалось в предыдущих ответах, в YAML нет встроенной поддержки для расширения списков.Я предлагаю еще один способ реализовать это самостоятельно.Учтите это:

defaults: &defaults
  sites:
    - www.foo.com
    - www.bar.com

setup1:
  <<: *defaults
  sites+:
    - www.baz.com

Это будет обработано в:

defaults:
  sites:
    - www.foo.com
    - www.bar.com

setup1:
  sites:
    - www.foo.com
    - www.bar.com
    - www.baz.com

Идея состоит в том, чтобы объединить содержимое ключа, заканчивающегося '+', с соответствующим ключом без+».Я реализовал это в Python и опубликовал здесь .

Наслаждайтесь!

6 голосов
/ 20 августа 2014

Чтобы пояснить что-то из двух ответов, это не поддерживается напрямую в YAML для списков (но поддерживается для словарей, см. Ответ kittemon)

4 голосов
/ 24 сентября 2014

Чтобы воспользоваться ответом Киттемона, обратите внимание, что вы можете создавать сопоставления с нулевыми значениями, используя альтернативный синтаксис

foo:
    << : myanchor
    bar:
    baz:

вместо предложенного синтаксиса

foo:
    << : myanchor
    ? bar
    ? baz

Как и предложение Киттемона, это позволит вам использовать ссылки на якоря в отображении и избежать проблемы последовательности. Я обнаружил, что должен сделать это, обнаружив, что компонент Symfony Yaml v2.4.4 не распознает синтаксис ? bar.

4 голосов
/ 17 февраля 2012

(Отвечая на мой собственный вопрос на тот случай, если решение, которое я использую, будет полезно всем, кто ищет его в будущем)

Не имея чистого способа YAML, я собираюсь реализовать это как «преобразование синтаксиса», которое находится между синтаксическим анализатором YAML и кодом, который фактически использует файл конфигурации. Таким образом, мое основное приложение не должно беспокоиться о каких-либо дружественных для человека мерах по избежанию избыточности и может просто воздействовать непосредственно на полученные структуры.

Структура, которую я собираюсь использовать, выглядит следующим образом:

foo:
  MERGE:
    - - a
      - b
      - c
    - - 1
      - 2
      - 3

Что будет преобразовано в эквивалент:

foo:
  - a
  - b
  - c
  - 1
  - 2
  - 3

Или с картами:

foo:
  MERGE:
    - fork: a
      spoon: b
      knife: c
    - cup: 1
      mug: 2
      glass: 3

Будет преобразовано в:

foo:
  fork: a
  spoon: b
  knife: c
  cup: 1
  mug: 2
  glass: 3

Более формально, после вызова синтаксического анализатора YAML для получения собственных объектов из файла конфигурации, но перед передачей объектов в остальную часть приложения мое приложение будет обходить граф объектов в поисках сопоставлений, содержащих один ключ MERGE. Значение, связанное с MERGE, должно быть либо списком списков, либо списком карт; любая другая подструктура является ошибкой.

В случае списка списков вся карта, содержащая MERGE, будет заменена дочерними списками, объединенными вместе в порядке их появления.

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

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

foo:
  MERGE:
    - *salt
    - *pepper

Позволяет вам создать список или карту, содержащую все в узлах salt и pepper, используемых в других местах.

(я продолжаю давать эту внешнюю карту foo:, чтобы показать, что MERGE должен быть ключом only в своем отображении, что означает, что MERGE не может отображаться в качестве имени верхнего уровня, если нет других имен верхнего уровня нет)

...