如何使用显式引用转储 YAML?
How to dump YAML with explicit references?
递归引用在 ruamel.yaml
或 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
所以我需要用 <<
替换 '<<'
并且可能用 foo
替换 id001
。
如果你想创建类似的东西,至少在 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']
意味着 data['foo']
中没有真正的键 'bar'
,但随后会在附加的 "merge mappings".
中查找该键
这里没有 public 接口(所以事情可能会改变),但是通过分析 data
并查看 ruamel.yaml.comments.CommentedMap()
你可以发现有一个 merge_attrib
(目前是字符串_yaml_merge
),更有用的是有一个方法add_yaml_merge()
。后者采用 (int, CommentedMap()) 元组列表。
baz = ruamel.yaml.comments.CommentedMap()
baz['b'] = 196
baz.yaml_set_anchor('klm')
data.insert(1, 'baz', baz)
需要在data的'bar'
键前插入'baz'
键,否则映射会反转。在 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)
。
¹ 免责声明:我是该软件包的作者。
递归引用在 ruamel.yaml
或 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
所以我需要用 <<
替换 '<<'
并且可能用 foo
替换 id001
。
如果你想创建类似的东西,至少在 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']
意味着 data['foo']
中没有真正的键 'bar'
,但随后会在附加的 "merge mappings".
这里没有 public 接口(所以事情可能会改变),但是通过分析 data
并查看 ruamel.yaml.comments.CommentedMap()
你可以发现有一个 merge_attrib
(目前是字符串_yaml_merge
),更有用的是有一个方法add_yaml_merge()
。后者采用 (int, CommentedMap()) 元组列表。
baz = ruamel.yaml.comments.CommentedMap()
baz['b'] = 196
baz.yaml_set_anchor('klm')
data.insert(1, 'baz', baz)
需要在data的'bar'
键前插入'baz'
键,否则映射会反转。在 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)
。
¹ 免责声明:我是该软件包的作者。