设置:
我有两个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
因为这不太适合字典(我认为?)
答案 0 :(得分:1)
如果小文件的键在大文件中是唯一的,则在大文件中遍历大文件的数据结构并更新其值相对简单:
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'
请注意:
id
个节点集,以防止无限递归。当然,只有在大数据是递归的情况下才有必要,但这不会造成损害。pop
update
的值,因此小文件为空,递归提前停止。如果您想更新所有个匹配键,则不必pop
,只需分配即可(然后您可以从walk_tree
中删除前两行)!Somestuff
类一无所知,标签也会保留。这是一种魔术!ruamel.yaml
以便始终转储锚点(通常,当还添加在数据树中多次表示的新元素时,这可能会导致冲突并需要进行额外的检查)yaml.indent
行的注释并调整值(以匹配大文件)或者,您可以使用一个类似以下内容的小型YAML:
[resources, that_thing, a, 1, some_stuff]: 42
[resources, some_other_stuff]: 'the_other_thing'
然后,您可以基于键序列元素对数据结构进行确定性递归,省去检查ID并沿update
拖动(只需将值作为walk_tree
的第二个参数传入)
如果所有更新都在大文件的顶层。然后,不需要进行任何递归,因为这只是上面的简单例子。