我有一些示例YAML,如下所示:
type:
- name: foo
location: bar
releases:
- name: app1
sha1: 11b318d4ec9f0baf75d8afc6f78cf66f955d459f
url: https://url.com/app.tar.gz
- name: app2
sha1: ef97bfaff05989ab006e88d28763feb8fbb32d45
url: https://url.com/app2.tar.gz
jobs:
- instances: 1
name: appname
templates:
- name: postgres
release: 1.0
我想合并一个YAML文件,该文件可能会添加到
中releases:
- name: app3
sha1: ef97bfaff05989ab006e88d28763feb8fbb32d45
url: https://url.com/app3.tar.gz
jobs:
-
templates:
- name: mysql
release: 1.0
我尝试将它们转换为dict
,然后将它们合并在一起,但这根本不起作用。
结尾应该是
type:
- name: foo
location: bar
releases:
- name: app1
sha1: 11b318d4ec9f0baf75d8afc6f78cf66f955d459f
url: https://url.com/app.tar.gz
- name: app2
sha1: ef97bfaff05989ab006e88d28763feb8fbb32d45
url: https://url.com/app2.tar.gz
- name: app3
sha1: ef97bfaff05989ab006e88d28763feb8fbb32d45
url: https://url.com/app3.tar.gz
jobs:
- instances: 1
name: appname
templates:
- name: postgres
release: 1.0
- name: mysql
release: 1.0
这是我作为dict
:
{'jobs': [{'instances': 1,
'name': 'appname',
'templates': [{'name': 'postgres', 'release': 1.0}]},
{'templates': [{'name': 'mysql', 'release': 1.0}]}],
'releases': [{'name': 'app1',
'sha1': '11b318d4ec9f0baf75d8afc6f78cf66f955d459f',
'url': 'https://url.com/app.tar.gz'},
{'name': 'app2',
'sha1': 'ef97bfaff05989ab006e88d28763feb8fbb32d45',
'url': 'https://url.com/app2.tar.gz'},
{'name': 'app3',
'sha1': 'ef97bfaff05989ab006e88d28763feb8fbb32d45',
'url': 'https://url.com/app3.tar.gz'}],
'type': [{'location': 'bar', 'name': 'foo'}]}
如果你注意到我的mysql模板不在postgres模板列表中,而是在另一个dict
中。
答案 0 :(得分:1)
我会使用递归扩展。如果您使用PyYaml读取第一个文件,然后将第二个文件读入不同的词典,则可以尝试以下操作:
def extend_dict(extend_me, extend_by):
if isinstance(extend_by, dict):
for k, v in extend_by.iteritems():
if k in extend_me:
extend_dict(extend_me.get(k), v)
else:
extend_me[k] = v
else:
extend_me += extend_by
extend_dict(file1, file2)
合并的结果将在file1
dict。
<强>更新强>
我添加了一些东西。它看起来有点乱,但我认为你可以在EXTENDABLE_KEYS
中添加你想要扩展的键。
使用正确的YAML文件对其进行测试,并告诉我它是否正常工作。
EXTENDABLE_KEYS = ('templates', )
def extend_dict(extend_me, extend_by):
if isinstance(extend_me, dict):
for k, v in extend_by.iteritems():
if k in extend_me:
extend_dict(extend_me[k], v)
else:
extend_me[k] = v
else:
if isinstance(extend_me, list):
extend_list(extend_me, extend_by)
else:
extend_me += extend_by
def extend_list(extend_me, extend_by):
missing = []
for item1 in extend_me:
if not isinstance(item1, dict):
continue
for item2 in extend_by:
if not isinstance(item2, dict) or item2 in missing:
continue
# Check if any key is an extendable key
if filter(lambda x: x in EXTENDABLE_KEYS, item1.keys()):
extend_dict(item1, item2)
else:
missing += [item2, ]
extend_me += missing
extend_dict(file1, file2)
print yaml.dump(file1, default_flow_style=False)
使用该片段,我获得以下内容:
type:
- location: bar
name: foo
releases:
- name: app1
sha1: 11b318d4ec9f0baf75d8afc6f78cf66f955d459f
url: https://url.com/app.tar.gz
- name: app2
sha1: ef97bfaff05989ab006e88d28763feb8fbb32d45
url: https://url.com/app2.tar.gz
- name: app3
sha1: ef97bfaff05989ab006e88d28763feb8fbb32d45
url: https://url.com/app3.tar.gz
jobs:
- instances: 1
name: appname
templates:
- name: postgres
release: 1.0
- name: mysql
release: 1.0
答案 1 :(得分:1)
您的示例输入文件中有3个序列,只有两个序列通过将序列从第二个示例扩展到第一个示例中的序列来“合并”。未合并的是序列,即jobs
键的值。
因此,您不能只是遍历从第二个示例加载的数据结构并“合并”您遇到的任何列表,您必须明确地执行此操作:
import sys
import ruamel.yaml as yaml
def update(l1, l2):
l1.extend(l2[:])
data1 = yaml.round_trip_load(open('1.yaml'))
data2 = yaml.round_trip_load(open('2.yaml'))
update(data1['releases'], data2['releases'])
update(data1['jobs'][0]['templates'], data2['jobs'][0]['templates'])
yaml.round_trip_dump(data1, sys.stdout)
带输出:
type:
- name: foo
location: bar
releases:
- name: app1
sha1: 11b318d4ec9f0baf75d8afc6f78cf66f955d459f
url: https://url.com/app.tar.gz
- name: app2
sha1: ef97bfaff05989ab006e88d28763feb8fbb32d45
url: https://url.com/app2.tar.gz
- name: app3
sha1: ef97bfaff05989ab006e88d28763feb8fbb32d45
url: https://url.com/app3.tar.gz
jobs:
- instances: 1
name: appname
templates:
- name: postgres
release: 1.0
- name: mysql
release: 1.0
这与您的预期不完全相同,因为您的输出示例不一致地缩进了type
的值的序列,与例如{1}}的值相比较。 releases
。
在上面,update()
可以很容易地递归(关于序列元素和映射值),但是必须有一些标准来选择哪些序列“合并”而哪些不合并(即{的值{1}})。
请注意,由于使用了jobs
,映射中键的顺序会自动保留。如果第一个文件会
有任何意见,这些也将被保留。第二个文件中作为合并序列一部分的任何行尾注释也会被保留。