如果小文件的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 的第二个参数传递)。
如果您的所有更新都在大文件的顶层。然后不需要任何递归,因为这只是上述的一个简单的极端情况。