如何使用ruamel.yaml自动添加引用?

How to automatically add a reference with ruamel.yaml?

前段时间我问了一个非常相似的问题,但我仍然对如何在 YAML 转储上添加引用感到困惑。

我的目标是在默认值上添加锚点,以尽量减少转储中的冗余。所以我写了这个:

import collections
import ruamel.yaml as yaml

default = {'a': 1, 'b': 2, 'c': 3}

data = {
    (1,2,3,4): {1: {'a': 10}, 2: {'b': 20}},
    (5,6,7,8): {1: {}, 2: {'a': 100, 'b': 200, 'c': 300}},
}

d = yaml.comments.CommentedMap()
d.update(default)
d.yaml_set_anchor('default')
default = d

for m, a in data.items():
    for k in a.keys():
        u = yaml.comments.CommentedMap()
        u.update(a[k])
        u.add_yaml_merge([(0, default)])
        a[k] = u

data[None] = default

def my_key_repr(self, data):
    if isinstance(data, tuple):
        return self.represent_sequence(u'tag:yaml.org,2002:seq', data, flow_style=True)
    return yaml.representer.SafeRepresenter.represent_key(self, data)

yaml.representer.RoundTripRepresenter.represent_key = my_key_repr

print yaml.dump(data, Dumper=yaml.RoundTripDumper, width=100, allow_unicode=True,
                explicit_start=True)

预期输出为:

---
~: &default
  a: 1
  c: 3
  b: 2
[1, 2, 3, 4]:
  1:
    <<: *default
    a: 10
  2:
    <<: *default
    b: 20
[5, 6, 7, 8]:
  1:
    <<: *default
  2:
    <<: *default
    a: 100
    c: 300
    b: 200

我得到的是:

---
?
: &default
  a: 1
  c: 3
  b: 2
[1, 2, 3, 4]:
  1:
    <<: *default
    a: 10
  2:
    <<: *default
    b: 20
&id001 [5, 6, 7, 8]:
  1:
    <<: *id001
  2:
    <<: *id001
    a: 100
    c: 300
    b: 200

*id001 不知从何而来...

这里有几个问题:

  • 如果您希望键 foobar 出现在您的输出中,您必须在您的源代码中指定它们。

  • 如果您希望 YAML 文档是隐式的(即不以 --- 开头),则不应指定 explicit_start=True

  • None 的密钥被转储为 ? 而不是 ~

  • 如果您使用 update()dict 填充 CommentedMap()(即有序字典),您不能期望将键添加到具体顺序。如果您希望按 acb 的顺序排列键(如您所指出的),则必须确保这是它们呈现给 CommentedMap()

  • 由于您的顶层 data 未排序,因此无法保证您的 YAML 文档中的顶层映射是 null 键(? :~:).

以下:

from ruamel import yaml

d = {'a': 1, 'b': 2, 'c': 3}

abc = yaml.comments.CommentedMap()
abc['a'] = 100
abc['c'] = 300
abc['b'] = 200

base = [
    ('foo', {1: {'a': 10}, 2: {'b': 20}}),
    ('bar', {1: {}, 2: abc}),
]

data = yaml.comments.CommentedMap()
default = yaml.comments.CommentedMap()
for m, a in base:
    data[m] = a
for k in sorted(d):
    default[k] = d[k]
default.yaml_set_anchor('default')


for m, a in data.items():
    for k in sorted(a.keys()):
        u = yaml.comments.CommentedMap()
        u.update(a[k])
        u.add_yaml_merge([(0, default)])
        a[k] = u

data.insert(0, None, default)

x = yaml.round_trip_dump(data, width=100).replace('?\n:', '~:')
print(x)

完全符合您的预期。