Подтвердить что ты не робот

Как объединить массивы YAML?

Я хотел бы объединить массивы в YAML и загрузить их через ruby ​​-

some_stuff: &some_stuff
 - a
 - b
 - c

combined_stuff:
  <<: *some_stuff
  - d
  - e
  - f

Я хотел бы иметь объединенный массив как [a,b,c,d,e,f]

Я получаю ошибку: не нашел ожидаемый ключ при разборе блочного отображения

Как объединить массивы в YAML?

4b9b3361

Ответ 1

Обновление: 2019-07-01 14:06:12

  • Примечание: другой ответ на этот вопрос был существенно отредактирован с обновлением альтернативных подходов.
    • Этот обновленный ответ упоминает альтернативу обходного пути в этом ответе. Он был добавлен в раздел " См. Также " ниже.

контекст

Этот пост предполагает следующий контекст:

  • Python 2.7
  • синтаксический анализатор Python YAML

проблема

lfender6445 желает объединить два или более списков в файле YAML, и эти проанализированные списки будут отображаться как единый список при разборе.

Решение (Обходной путь)

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

В приведенном ниже примере у нас есть три сопоставления (list_one, list_two, list_three) и три привязки и псевдонимы, которые ссылаются на эти сопоставления, где это необходимо.

Когда файл YAML загружен в программу, мы получаем список, который нам нужен, но он может потребовать небольшой модификации после загрузки (см. Подводные камни ниже).

пример

Оригинальный файл YAML

  list_one: &id001
   - a
   - b
   - c

  list_two: &id002
   - e
   - f
   - g

  list_three: &id003
   - h
   - i
   - j

  list_combined:
      - *id001
      - *id002
      - *id003

Результат после YAML.safe_load

## list_combined
  [
    [
      "a",
      "b",
      "c"
    ],
    [
      "e",
      "f",
      "g"
    ],
    [
      "h",
      "i",
      "j"
    ]
  ]

Ловушки

  • при таком подходе создается вложенный список списков, который может не соответствовать желаемому результату, но может быть подвергнут последующей обработке с использованием метода flatten
  • обычные оговорки к якорям и псевдонимам YAML относятся к уникальности и порядку объявления

Заключение

Этот подход позволяет создавать объединенные списки с использованием псевдонима и функции привязки YAML.

Хотя результат вывода представляет собой вложенный список списков, его можно легко преобразовать с помощью метода flatten.

Смотрите также

Обновленный альтернативный подход @Anthon

Примеры метода flatten

Ответ 2

Это не сработает

  1. Слияние поддерживается только спецификациями YAML для отображений, а не для последовательностей.

  2. Вы полностью смешиваете вещи, имея ключ слияния << за которым следует разделитель ключ/значение : и значение, которое является ссылкой, а затем продолжаете со списком на том же уровне отступа.

Это не правильно 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 использует слияния грубой силы во время загрузки при использовании ее безопасного загрузчика, что приводит к объединенным словарям, которые являются обычными диктатами Python. Это объединение должно быть выполнено заранее и дублирует данные (неэффективное пространство), но быстро в поиске значений. При использовании кругового загрузчика вы хотите иметь возможность выгружать сливы без объединения, поэтому их необходимо хранить отдельно. Диктофон, подобный структуре данных, загруженной в результате круговой загрузки, является эффективным с точки зрения пространства, но более медленным в доступе, так как он должен попытаться найти ключ, не найденный в самом dict в слияниях (и это не кэшируется, поэтому нужно делать каждый раз). Конечно, такие соображения не очень важны для относительно небольших файлов конфигурации.


Следующее реализует схему, подобную слиянию, для списков в 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 на их описания на английском (или другом естественном языке).

Ответ 3

Вы можете добиться этого следующим образом:

# note: no dash before commands
some_stuff: &some_stuff |-
    a
    b
    c

combined_stuff:
  - *some_stuff
  - d
  - e
  - f

Я использовал это на своем gitlab-ci.yml (чтобы ответить на @rink.attendant.6 комментарий по этому вопросу).

Ответ 4

Если вам нужно объединить только один элемент в список, вы можете сделать

fruit:
  - &banana
    name: banana
    colour: yellow

food:
  - *banana
  - name: carrot
    colour: orange

который дает

fruit:
  - name: banana
    colour: yellow

food:
  - name: banana
    colour: yellow
  - name: carrot
    colour: orange

Ответ 5

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

  • Если вы используете шаблоны jinja2 и
  • если порядок элементов не важен
some_stuff: &some_stuff
 a:
 b:
 c:

combined_stuff:
  <<: *some_stuff
  d:
  e:
  f:

{{ combined_stuff | list }}