python合并复杂YAML

python合并复杂YAML,python,yaml,Python,Yaml,我有一些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/ap

我有一些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
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,然后将它们合并在一起,但根本不起作用

结局应该是这样的

releases:
- name: app3
  sha1: ef97bfaff05989ab006e88d28763feb8fbb32d45
  url: https://url.com/app3.tar.gz
jobs:
-
  templates:
  - name: mysql
    release: 1.0
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
这就是我作为一名
口述者得到的:

{'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

我会选择递归扩展。如果使用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)
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
合并的结果将出现在
file1
dict中

更新:

我加了一些东西。它看起来有点混乱,但我认为您可以在
extendeable\u 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)
通过该片段,我获得以下信息:

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)
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

在您的示例输入文件中有3个序列,通过将序列从第二个示例扩展到第一个示例,其中只有两个得到“合并”。未合并的是作为
作业
键的值的序列

因此,您不能只是浏览从第二个示例加载的数据结构,然后“合并”遇到的任何列表,您必须明确地执行此操作:

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
这与您期望的不完全一致,因为您的输出示例与例如发布的值相比,缩进的序列是
类型
的值

在上面的例子中,
update()
可以很容易地进行递归(在序列元素和映射值上),但是必须有一些标准来选择哪些序列要“合并”,哪些不合并(即
作业的值

请注意,由于使用了
往返加载()
映射中键的顺序将自动保留。如果第一个文件
如果您有任何意见,也将保留这些意见。第二个文件中作为合并序列一部分的任何行尾注释也会保留。

您看过吗?@MattDMo PyYAML将在转储合并代码时扰乱映射中键的顺序。除此之外,PyYAML只支持旧的YAML 1.1(2005年,以及有限制的版本),而不支持最新的YAML 1.2(从2009年开始),不可否认,这并不影响OP提供的示例YAML代码。@Anthon right,我已经忘记了这一点。当然,dict也不会维护您数据的顺序…我更新以显示我所看到的。。但你的例子就是我得到的模板部分的另一个列表,用适当的文件测试它,并让我知道它是否有效:)