Це не спрацює:
Злиття підтримується лише специфікаціями YAML для відображень, а не для послідовностей
ви повністю змішуєте речі, маючи ключ злиття, <<
за яким слід роздільник ключ / значення :
та значення, яке є еталонним, а потім продовжуйте зі списком на тому ж рівні відступу
Це неправильно YAML:
combine_stuff:
x: 1
- a
- b
Тож ваш синтаксис прикладу навіть не мав би сенсу як пропозиція щодо розширення YAML.
Якщо ви хочете зробити щось на зразок об'єднання декількох масивів, ви можете розглянути такий синтаксис:
combined_stuff:
- <<: *s1, *s2
- <<: *s3
- d
- e
- f
де s1
, s2
, s3
є якорями на послідовностях (не показані) , які ви хочете об'єднати в нову послідовність і потім мати d
, e
і f
прикладені до цього. Але YAML вирішує цю структуру глибиною спочатку, тому під час обробки ключа злиття немає реального контексту. Для вас немає масиву / списку, до якого можна було б долучити оброблене значення (приєднану послідовність).
Ви можете прийняти підхід, запропонований @dreftymac, але це має величезний недолік, який вам потрібно якось знати, які вкладені послідовності вирівняти (тобто знаючи "шлях" від кореня завантаженої структури даних до батьківської послідовності), або що ви рекурсивно ходите по завантаженій структурі даних, шукаючи вкладені масиви / списки і без розбору вирівнювати їх усі.
Кращим рішенням IMO було б використовувати теги для завантаження структур даних, які роблять для вас вирівнювання. Це дозволяє чітко позначати, що потрібно вирівняти, а що ні, і надає повний контроль над тим, чи відбувається це вирівнювання під час завантаження чи це робиться під час доступу. Який вибрати - це питання простоти впровадження та ефективності в часі та місцях зберігання. Це той самий компроміс, який потрібно здійснити для реалізації ключової функції об'єднання, і не існує єдиного рішення, яке завжди є найкращим.
Наприклад, моя ruamel.yaml
бібліотека використовує грубі сили merge-dicts під час завантаження при використанні свого безпечного завантажувача, що призводить до об'єднання словників, які є нормальними диктовками Python. Це злиття повинно здійснюватися наперед і дублює дані (неефективне місце), але швидко шукає значення. Використовуючи навантажувач із зворотним ходом, ви хочете мати змогу скинути злиття без накиду, тому їх потрібно тримати окремо. Диктування, подібна до структури даних, завантаженої в результаті завантаження в зворотній бік, є просторовим, але повільнішим у доступі, оскільки для цього потрібно спробувати і знайти ключ, який не знайдений у самому диктаті в злиттях (і це не кешовано, так що потрібно робити щоразу). Звичайно такі міркування не дуже важливі для відносно невеликих файлів конфігурації.
Далі реалізується схема подібного злиття для списків у python, використовуючи об’єкти з тегом, flatten
які на ходу рекурсують у елементи, що є списками та позначеними тегами toflatten
. За допомогою цих двох тегів ви можете мати файл YAML:
l1: &x1 !toflatten
- 1
- 2
l2: &x2
- 3
- 4
m1: !flatten
- *x1
- *x2
- [5, 6]
- !toflatten [7, 8]
(використання послідовностей стилю потоку проти блоку є абсолютно довільним і не впливає на завантажений результат).
При ітерації над елементами, які є значенням для ключа, m1
це "повторюється" у послідовності, позначені тегом toflatten
, але відображає інші списки (псевдонім чи ні) як один елемент.
Один з можливих способів за допомогою коду Python досягти цього:
import sys
from pathlib import Path
import ruamel.yaml
yaml = ruamel.yaml.YAML()
@yaml.register_class
class Flatten(list):
yaml_tag = u'!flatten'
def __init__(self, *args):
self.items = args
@classmethod
def from_yaml(cls, constructor, node):
x = cls(*constructor.construct_sequence(node, deep=True))
return x
def __iter__(self):
for item in self.items:
if isinstance(item, ToFlatten):
for nested_item in item:
yield nested_item
else:
yield item
@yaml.register_class
class ToFlatten(list):
yaml_tag = u'!toflatten'
@classmethod
def from_yaml(cls, constructor, node):
x = cls(constructor.construct_sequence(node, deep=True))
return x
data = yaml.load(Path('input.yaml'))
for item in data['m1']:
print(item)
який виводить:
1
2
[3, 4]
[5, 6]
7
8
Як ви бачите, що ви бачите, у послідовності, яка потребує вирівнювання, ви можете використовувати псевдонім до послідовності з тегом, або ви можете використовувати позначену послідовність. YAML не дозволяє:
- !flatten *x2
, тобто позначає прив'язану послідовність, оскільки це по суті перетворить її в іншу структуру даних.
Використовувати явні теги - IMO краще, ніж робити магію, як і для клавіш злиття YAML <<
. Якщо нічого іншого, вам зараз доведеться пройти обручі, якщо у вас випадково є файл YAML з відображенням, який містить ключ,
<<
який ви не хочете діяти як ключ злиття, наприклад, коли ви робите відображення операторів C на їх описи англійською (або якоюсь іншою природною мовою).