【问题标题】:Updating a yaml file with the contents of another one用另一个文件的内容更新一个 yaml 文件
【发布时间】:2019-01-09 04:58:56
【问题描述】:

设置:
我有两个 YAML 文件:一个带有标签和别名的大文件,另一个带有一些来自大文件的键值对的小文件。我正在使用python2.7。

问题:
我想用小值中的值更新大值中的值。

挑战:
小 yaml 可以包含大 yaml 中存在的键值对的任意组合。我还必须保留大的文字结构(不解析标签/别名)。大的很复杂,包含字典列表的字典(不要问...)。这可能吗?

尤其是这样的事情:

resources: &something
   that_thing: some_stuff
   some_other_stuff: this_thing

我想得到的例子:

resources: &something
   that_thing: some_stuff
   some_other_stuff: this_thing_updated

因为这不太适合字典(我认为?)

【问题讨论】:

  • 如果key-value对必须存在于big中,则不需要更新。我假设您的意思是密钥必须存在于大密钥中,并且其值会使用小密钥中的密钥更新。大文件中小文件的key在哪里?在顶层或树中的任何位置(在这种情况下,它们在大文件中也必须是全局“唯一的”才能更新它们)。大文件中的所有anchors实际上都是别名吗?

标签: python python-2.7 yaml pyyaml ruamel.yaml


【解决方案1】:

如果小文件的keys在大文件中是唯一的,那么遍历大文件的数据结构并更新keys的值相对简单:

import sys
import ruamel.yaml

big_yaml = """\
resources: &something
   that_thing: !SomeStuff 
      a:
      - 1
      - some_stuff: d
      b: *something
   some_other_stuff: this_thing
"""

small_yaml = """\
some_stuff: 42
some_other_stuff: 'the_other_thing'
"""

def walk_tree(d, update, done=set()):
    if not update:
        return
    if id(d) in done:
        return
    if isinstance(d, dict):
        done.add(id(d))
        for k in d:
            if k in update:
                d[k] = update.pop(k)
                continue  # don't recurse in the newly updated value
            walk_tree(d[k], update)  # recurse into the values
    elif isinstance(d, list):
        done.add(id(d))
        for elem in d:
            walk_tree(elem, update)
    # doing nothing for leaf-node scalars


yaml = ruamel.yaml.YAML()
# yaml.indent(mapping=2, sequence=2, offset=0)
yaml.preserve_quotes = True
big = yaml.load(big_yaml)
small = yaml.load(small_yaml)
# print(data)
walk_tree(big, small)

yaml.dump(big, sys.stdout)

给出:

resources: &something
  that_thing: !SomeStuff
    a:
    - 1
    - some_stuff: 42
    b: *something
  some_other_stuff: 'the_other_thing'

请注意:

  • 您需要保留节点的ids 集合,以防止无限递归。当然,只有在您的大数据是递归的情况下才需要这样做,但不会有坏处。
  • popupdate的值,所以小文件变空,递归提前停止。如果你想更新所有匹配的键,那么不要pop,只需分配(然后你可以从walk_tree中删除前两行)
  • 标签被保留,即使程序对如何创建!Somestuff 类一无所知。这是一种魔法!
  • 当前仅当存在引用它的实际别名时才会转储锚点。锚点被保留,因此ruamel.yaml 可能会被修补以始终转储锚点(通常,当还在数据树中添加多次表示的新元素时,这可能会导致冲突并需要额外检查)
  • 保留了围绕“the_other_thing”的多余引用
  • 您的映射对应。序列缩进需要保持一致,否则它们会被重新格式化。您可以取消注释 yaml.indent 行并调整值(以匹配大文件)

或者,您可以使用如下所示的小型 YAML:

[resources, that_thing, a, 1, some_stuff]: 42
[resources, some_other_stuff]: 'the_other_thing'

然后您可以根据键序列元素对数据结构进行确定性递归,无需检查 id 并沿 update 拖动(只需将值作为 walk_tree 的第二个参数传递)。


如果您的所有更新都在大文件的顶层。然后不需要任何递归,因为这只是上述的一个简单的极端情况。

【讨论】:

  • ... 只是发表评论以表达我的感激之情。我认为这太不可思议了,有些人在他们的答案中付出了多少努力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-25
  • 2013-02-10
  • 2014-05-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多