Чи існує синтаксис YAML для спільного використання частини списку або карти?


94

Отже, я знаю, що можу зробити щось подібне:

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

Набори не є відображенням; відображення - це набір асоціацій ключ-значення. Коли я yaml.load(...)працюю на Python, я отримую словник як подання відображення YAML. Так, це легко переробити в набір, але я повинен знати, що це сталося (і семантична складність під час читання / запису конфігураційних файлів набагато вища, якщо правило "набори записуються як карти з нульовими значеннями" ). Враховуючи, що мені потрібна подальша обробка між yaml.load(...)і використанням отриманих даних, незалежно від того, використовую я <<чи MERGE, я, мабуть, дотримуюся MERGE(що я вже реалізував зараз).
Бен,

2
Так, я знайшов, що це !!setпрацює. Занадто багато незрозумілого шаблону. Ці файли створені для читання / запису людей, які не обов'язково є експертами YAML. Люди збираються записувати свої списки сайтів як списки YAML, потім хочуть їх об’єднати і повинні перетворити все це на набір І пам’ятати, що явно позначають це як набір ... У мене є ще кілька стандартизованих пост- обробка речей разом із MERGEбудь-яким. Дякую за вашу допомогу!
Бен,

16

Як зазначалося в попередніх відповідях, в 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

11
Чи означає це, що цей підхід працює лише з окремим інструментом, який об'єднує sitesта sites+. Я маю на увазі інструмент, який повинен впроваджувати користувач, оскільки це не yamlповедінка за замовчуванням ?
stan0

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

Щоб вимкнути відповідь Кіттемона, зверніть увагу, що ви можете створювати зіставлення з нульовими значеннями, використовуючи альтернативний синтаксис

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.