2

前段时间我问了一个非常相似的问题,但我仍然对如何在 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从何而来……

4

1 回答 1

2

这里有几个问题:

  • 如果您希望密钥foobar显示在输出中,则必须在源中的某处指定它们。

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

  • 为 None 的键被转储为?not as~

  • 如果您使用从 aupdate()填充CommentedMap()(即有序字典)dict,则不能期望以特定顺序添加键。如果您希望按键按顺序a, c, b(如您所指),您必须确保这是它们呈现给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)

准确地给出您期望的输出。

于 2017-05-04T17:02:23.307 回答