6

递归引用在ruamel.yamlor中效果很好pyyaml

$ ruamel.yaml.dump(ruamel.yaml.load('&A [ *A ]'))
'&id001
- *id001'

但是它(显然)不适用于普通参考:

$ ruamel.yaml.dump(ruamel.yaml.load("foo: &foo { a: 42 }\nbar: { <<: *foo }"))
bar: {a: 42}
foo: {a: 42}

我想明确地创建一个参考:

data = {}
data['foo'] = {'foo': {'a': 42}}
data['bar'] = { '<<': data['foo'], 'b': 43 }

$ ruamel.yaml.dump(data, magic=True)
foo: &foo
    a: 42
bar: 
    <<: *foo
    b: 43

这对于生成具有大量公共键的大型数据结构的 YAML 输出非常有用

输出上没有有争议的 re.replace 怎么可能?

实际上的结果ruamel.yaml.dump(data)

bar:
  '<<': &id001
    foo:
      a: 42
  b: 43
foo: *id001

所以我需要替换'<<'<<,也许替换id001foo.

4

1 回答 1

4

如果你想创建类似的东西,至少在 ruamel.yaml 中,你应该使用往返模式,这也保留了合并。以下不会引发断言错误:

import ruamel.yaml

yaml_str = """\
foo: &xyz
  a: 42
bar:
  <<: *xyz
"""

data = ruamel.yaml.round_trip_load(yaml_str)
assert ruamel.yaml.round_trip_dump(data) == yaml_str

这意味着data有足够的信息来重新创建输出中的合并。然而,在实践中,在往返模式下,永远不会发生合并。相反,检索一个值data['foo']['bar']['a']意味着 中没有真正的键'bar'data['foo']但随后会在附加的“合并映射”中查找该键。

这个没有公共接口(所以事情可能会改变),但是通过分析data和查看ruamel.yaml.comments.CommentedMap()你会发现有一个merge_attrib(当前是 string _yaml_merge)并且更有用的是有一个 method add_yaml_merge()。后者采用 (int, CommentedMap()) 元组的列表。

baz = ruamel.yaml.comments.CommentedMap()
baz['b'] = 196
baz.yaml_set_anchor('klm')
data.insert(1, 'baz', baz)

您需要在数据'baz'键之前插入键'bar',否则映射会反转。在合并中插入新结构后data['bar']

data['bar'].add_yaml_merge([(0, baz)])
ruamel.yaml.round_trip_dump(data, sys.stdout)

这使:

foo: &xyz
  a: 42
baz: &klm
  b: 196
bar:
  <<: [*xyz, *klm]

(如果你想看看add_yaml_merge插入了什么

print(getattr(data['bar'], ruamel.yaml.comments.merge_attrib))

通话前后)

如果你想完全从头开始,你可以这样做:

data = ruamel.yaml.comments.CommentedMap([
    ('foo', ruamel.yaml.comments.CommentedMap([('a', 42)])),
    ])
data['foo'].yaml_set_anchor('xyz')
data['bar'] = bar = ruamel.yaml.comments.CommentedMap()
bar.add_yaml_merge([(0, data['foo'])])

而不是data = ruamel.yaml.round_trip_load(yaml_str).


¹免责声明:我是该软件包的作者。

于 2016-09-16T16:06:53.273 回答