在Python中控制Yaml序列化顺序

时间:2011-12-28 01:59:18

标签: python yaml

在序列化Python字典时,如何控制PyYaml输出键/值对的顺序?

我在Python脚本中使用Yaml作为简单的序列化格式。我的Yaml序列化对象代表了一种“文档”,因此为了最大限度地方便用户,我希望我的对象的“名称”字段首先出现在文件中。当然,由于我的对象__getstate__返回的值是字典,而Python字典是无序的,因此“name”字段将序列化为输出中的随机位置。

e.g。

>>> import yaml
>>> class Document(object):
...     def __init__(self, name):
...         self.name = name
...         self.otherstuff = 'blah'
...     def __getstate__(self):
...         return self.__dict__.copy()
... 
>>> doc = Document('obj-20111227')
>>> print yaml.dump(doc, indent=4)
!!python/object:__main__.Document
otherstuff: blah
name: obj-20111227

5 个答案:

答案 0 :(得分:17)

花了几个小时挖掘PyYAML文档和门票,但我最终发现this comment列出了一些概念验证代码,用于将OrderedDict序列化为普通的YAML映射(但保持顺序)

e.g。应用于我的原始代码,解决方案看起来像:

>>> import yaml
>>> from collections import OrderedDict
>>> def dump_anydict_as_map(anydict):
...     yaml.add_representer(anydict, _represent_dictorder)
... 
>>> def _represent_dictorder( self, data):
...     if isinstance(data, Document):
...         return self.represent_mapping('tag:yaml.org,2002:map', data.__getstate__().items())
...     else:
...         return self.represent_mapping('tag:yaml.org,2002:map', data.items())
... 
>>> class Document(object):
...     def __init__(self, name):
...         self.name = name
...         self.otherstuff = 'blah'
...     def __getstate__(self):
...         d = OrderedDict()
...         d['name'] = self.name
...         d['otherstuff'] = self.otherstuff
...         return d
... 
>>> dump_anydict_as_map(Document)
>>> doc = Document('obj-20111227')
>>> print yaml.dump(doc, indent=4)
!!python/object:__main__.Document
name: obj-20111227
otherstuff: blah

答案 1 :(得分:5)

我认为问题是当您转储数据时。 我查看了PyYaml的代码,然后有一个名为sort_keys的可选参数,将该值设置为False似乎可以解决问题。

答案 2 :(得分:2)

新解决方案(自 2020 和PyYAML 5.1起)

只需使用

,就可以按其当前顺序转储字典。
yaml.dump(data, default_flow_style=False, sort_keys=False)

答案 3 :(得分:-1)

Cerin,非常感谢您的回答,它帮助我解决了我的问题。但是我花了一些时间来理解答案,因为没有提到输入字典。因此,我将使用输入字典重新发布@cerin的答案。在这里,输出显示为单独的条目。因此,此方法适用于以预定义的顺序将数据递归转储到yaml文件中。

import yaml

input_dict = {"first_key": "fist_value", "second_key": "second_value", "third_key": "third_value"}

from collections import OrderedDict
def dump_anydict_as_map(anydict):
    yaml.add_representer(anydict, _represent_dictorder)

def _represent_dictorder( self, data):
    if isinstance(data, Document):
        return self.represent_mapping('tag:yaml.org,2002:map', data.__getstate__().items())
    else:
        return self.represent_mapping('tag:yaml.org,2002:map', data.items())

class Document(object):
    def __init__(self, name): # no need to preserve the order here
        self.first_key = input_dict["first_key"]
        self.second_key = input_dict["second_key"]
        self.third_key = input_dict["third_key"]
    def __getstate__(self): # this is where order should be defined
        d = OrderedDict()
        d['second_key'] = self.second_key
        d['third_key'] = self.third_key
        d['first_key'] = self.first_key
        return d

dump_anydict_as_map(Document)
doc = Document('obj-20111227')
print(yaml.dump([doc], default_flow_style=False))

输出

- second_key: second_value
  third_key: third_value
  first_key: fist_value

答案 4 :(得分:-8)

我最后一次检查时,Python的字典没有被订购。如果您真的想要它们,我强烈建议使用键/值对列表。

[
    ('key', 'value'),
    ('key2', 'value2')
]

或者,使用键定义列表并按正确的顺序排列。

keys = ['key1', 'name', 'price', 'key2'];
for key in keys:
    print obj[key]