Есть ли синтаксис 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

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


Ах, спасибо! Это очень полезно. Жаль, что это не работает для последовательностей. Вы правы, в этом примере порядок не важен; То, что у меня есть, концептуально представляет собой набор, но он гораздо больше соответствует последовательности, чем отображению. И структура того, что я получаю от этого, имеет значение (поэтому я не хотел просто добавлять еще один уровень вложенности для слияния моих структур), поэтому наличие сопоставления, для которого мне нужно игнорировать (все нулевые) значения, не действительно не работает.
Бен

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


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

5

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

foo:
    << : myanchor
    bar:
    baz:

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

foo:
    << : myanchor
    ? bar
    ? baz

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


как myanchorвыглядит?
ssc
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.