我想在YAML中合并数组,并通过ruby加载它们-
some_stuff: &some_stuff
- a
- b
- c
combined_stuff:
<<: *some_stuff
- d
- e
- f
我想将组合数组作为 [a,b,c,d,e,f]
我收到错误:解析块映射时未找到预期的密钥
如何在YAML中合并数组?
我想在YAML中合并数组,并通过ruby加载它们-
some_stuff: &some_stuff
- a
- b
- c
combined_stuff:
<<: *some_stuff
- d
- e
- f
我想将组合数组作为 [a,b,c,d,e,f]
我收到错误:解析块映射时未找到预期的密钥
如何在YAML中合并数组?
Answers:
如果目的是运行一系列Shell命令,则可以按照以下步骤实现:
# note: no dash before commands
some_stuff: &some_stuff |-
a
b
c
combined_stuff:
- *some_stuff
- d
- e
- f
这等效于:
some_stuff: "a\nb\nc"
combined_stuff:
- "a\nb\nc"
- d
- e
- f
我已经在我身上使用了 gitlab-ci.yml
(回答有关问题的@ rink.attendant.6评论)。
我们用来支持requirements.txt
从gitlab获得私有仓库的工作示例:
.pip_git: &pip_git
- git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com".insteadOf "ssh://git@gitlab.com"
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
test:
image: python:3.7.3
stage: test
script:
- *pip_git
- pip install -q -r requirements_test.txt
- python -m unittest discover tests
use the same `*pip_git` on e.g. build image...
其中requirements_test.txt
包含例如
-e git+ssh://git@gitlab.com/example/example.git@v0.2.2#egg=example
这篇文章假设以下情况:
lfender6445希望在一个YAML文件中合并两个或多个列表,并在解析时使那些合并的列表显示为一个单数列表。
这可以简单地通过将YAML锚分配给映射来获得,其中所需列表显示为映射的子元素。但是,有一些注意事项(请参阅下面的“陷阱”)。
在下面的示例中,我们有三个映射(list_one, list_two, list_three
)和三个锚点和别名,它们在适当时引用了这些映射。
将YAML文件加载到程序中后,我们会获得所需的列表,但是加载后可能需要进行一些修改(请参见下面的陷阱)。
list_one:&id001 - 一个 -b - C list_two:&id002 -e - F - G 清单三:&id003 - H - 一世 -j list_combined: -* id001 -* id002 -* id003
## list_combined [ [ “一个”, “ b”, “C” ], [ “ e”, “F”, “G” ], [ “H”, “一世”, “ j” ] ]
这种方法允许使用YAML的别名和锚点功能创建合并列表。
尽管输出结果是列表的嵌套列表,但是可以使用该flatten
方法轻松地对其进行转换。
flatten
方法实例flatten
;; 合并/展平数组flatten
;; http://ruby-doc.org/core-2.2.2/Array.html#method-i-flattenflatten
;; https://softwareengineering.stackexchange.com/a/254676/23884这是行不通的:
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
库在使用安全加载程序时在加载过程中使用了蛮力合并字典,这导致合并的字典是普通的Python字典。这种合并必须预先完成,并且会复制数据(空间效率低下),但查找值的速度很快。使用双向加载程序时,您希望能够转储未合并的合并,因此需要将它们分开。像数据结构这样的dict由于往返加载而被加载,它是空间有效的,但是访问速度较慢,因为它需要尝试查找合并中dict本身未找到的键(并且该键不会被缓存,因此它每次都需要做)。当然,这些考虑对于相对较小的配置文件而言不是很重要。
以下代码使用带有标签的对象在python中为列表实现了类似于合并的方案,该对象flatten
可即时递归到list和tag的项目中toflatten
。使用这两个标记,您可以拥有YAML文件:
l1: &x1 !toflatten
- 1
- 2
l2: &x2
- 3
- 4
m1: !flatten
- *x1
- *x2
- [5, 6]
- !toflatten [7, 8]
(使用流vs块样式序列完全是任意的,并且对加载的结果没有影响)。
遍历键值的项目时,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
,即标记锚定的序列,因为这实际上会使它变成不同的数据结构。
与使用YAML合并键进行一些魔术操作相比,使用IMO 显式标签更好<<
。如果您碰巧拥有一个YAML文件,且其映射的键<<
不想让您像合并键一样工作,例如,当您将C运算符映射到它们的描述时,如果现在没有其他事情,那么现在您就必须经历麻烦
了。用英语(或其他自然语言)。
在以下情况下,您可以合并映射,然后将其键转换为列表:
some_stuff: &some_stuff
a:
b:
c:
combined_stuff:
<<: *some_stuff
d:
e:
f:
{{ combined_stuff | list }}