Есть ли синтаксис YAML для совместного использования части списка или карты?

95

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

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, чтобы помочь с этим, на случай, если кто-то найдет этот вопрос в будущем.

Бен
источник
Примечание. Эта проблема также может быть решена с помощью стандартного использования якорей и псевдонимов в YAML. См. Также: Как объединить массивы YAML?
dreftymac

Ответы:

53

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

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

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

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

Kittemon
источник
Ах, спасибо! Это очень полезно. Жаль, что это не работает для последовательностей. Вы правы, в этом примере порядок не важен; То, что у меня есть, концептуально представляет собой набор, но он гораздо больше соответствует последовательности, чем отображению. И структура того, что я получаю от этого, имеет значение (поэтому я не хотел просто добавлять еще один уровень вложенности для слияния моих структур), поэтому наличие сопоставления, для которого мне нужно игнорировать (все нулевые) значения, не действительно не работает.
Бен
3
Я ничего не вижу в текущей официальной спецификации YAML: yaml.org/spec/1.2/spec.html . На этой странице нет ни слова «объединить», ни текста «<<», ни фразы «тип ключа». Однако синтаксис << работает в пакете Python yaml. Вы знаете, где я могу узнать больше об этих дополнительных функциях?
Бен
1
Это не прямо в спецификации, это описано в репозитории тегов. Другие схемы имеют общее описание и ссылку. Помимо ключей слияния, есть также наборы и упорядоченные наборы; однако YAML рассматривает наборы как тип отображения (например, приведенный выше пример может быть реализован как набор). Позволяет ли ваш язык поменять местами ключи со значениями в результирующем отображении? Даже если вам придется реализовать это самостоятельно, я думаю, это будет чище; вы бы, по крайней мере, уже сгруппировали все данные вместе, и ваш YAML был бы стандартным.
kittemon 02
Однако наборы не являются отображениями; отображение - это набор ассоциаций "ключ-значение". Когда я использую yaml.load(...)Python, я получаю словарь как представление отображения YAML. Да, это легко постобработать в набор, но я должен знать, что это произошло (и семантическая сложность при чтении / записи файлов конфигурации намного выше, если правило: «наборы записываются как карты с нулевыми значениями» ). Учитывая, что мне нужна пост-обработка между yaml.load(...)полученными данными и их использование, независимо от того, использую я <<или MERGE, я, вероятно, буду придерживаться MERGE(что я уже реализовал сейчас).
Бен
2
Да, я нашел, что это !!setработает. Однако слишком много непонятного шаблона. Эти файлы предназначены для чтения и записи людьми, которые не обязательно являются экспертами по YAML. Люди собираются записать свои списки сайтов в виде списков YAML, затем захотят объединить их и преобразовать все это в набор И не забудьте явно пометить его как набор ... У меня есть пара других стандартизированных пост- обрабатывая вещи вместе с MERGEлюбым. Спасибо за вашу помощь!
Бен
17

Как указывалось в предыдущих ответах, в 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 и опубликовал здесь .

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

Александр Рыжов
источник
2
Примечание. Эта проблема также может быть решена с помощью стандартного использования якорей и псевдонимов в YAML. См. Также: Как объединить массивы YAML?
dreftymac
12
Означает ли это, что этот подход работает только с отдельным инструментом, который объединяет sitesи sites+. Я имею в виду инструмент, который должен быть реализован пользователем, поскольку это не yamlповедение по умолчанию ?
stan0 02
7

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

Поскольку для этого нет способа с использованием чистого 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должен быть единственный ключ в ее сопоставлении, что означает, что оно MERGEне может отображаться как имя верхнего уровня, если нет других имен верхнего уровня)

Бен
источник
6

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

Asmeurer
источник
Примечание. Эта проблема также может быть решена с помощью стандартного использования якорей и псевдонимов в YAML. См. Также: Как объединить массивы YAML?
dreftymac
5

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

foo:
    << : myanchor
    bar:
    baz:

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

foo:
    << : myanchor
    ? bar
    ? baz

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

beef_boolean
источник
как myanchorвыглядит?
ssc