我可以在json。dump中使用OrderedDict。也就是说,OrderedDict可以用作JSON的输入。

但它能被用作输出吗?如果是,怎么做?在我的情况下,我想加载到OrderedDict,这样我就可以保持文件中键的顺序。

如果不是,有没有什么解决办法?


除了转储字典之外,您总是可以写出键的列表,然后通过遍历列表重新构造OrderedDict ?


是的,你可以。通过将object_pairs_hook参数指定给JSONDecoder。事实上,这正是文档中给出的示例。

>>> json.JSONDecoder(object_pairs_hook=collections.OrderedDict).decode('{"foo":1, "bar": 2}')
OrderedDict([('foo', 1), ('bar', 2)])
>>> 

你可以把这个参数传递给json。加载(如果你不需要Decoder实例用于其他目的),如下所示:

>>> import json
>>> from collections import OrderedDict
>>> data = json.loads('{"foo":1, "bar": 2}', object_pairs_hook=OrderedDict)
>>> print json.dumps(data, indent=4)
{
    "foo": 1,
    "bar": 2
}
>>> 

使用json。加载的方式相同:

>>> data = json.load(open('config.json'), object_pairs_hook=OrderedDict)

Python 2.7+的简单版本

my_ordered_dict = json.loads(json_str, object_pairs_hook=collections.OrderedDict)

或者适用于Python 2.4到2.6

import simplejson as json
import ordereddict

my_ordered_dict = json.loads(json_str, object_pairs_hook=ordereddict.OrderedDict)

除了在字典旁边转储键的有序列表外,另一个低技术含量的解决方案(具有显式的优点)是转储键-值对的有序列表ordered_dict.items();加载是一个简单的OrderedDict(<键值对列表>)。这处理了一个有序字典,尽管JSON没有这个概念(JSON字典没有顺序)。

利用json以正确的顺序转储OrderedDict这一事实确实很好。然而,作为OrderedDict(通过object_pairs_hook参数)读取所有JSON字典通常是不必要的沉重,也没有必要的意义,因此仅显式转换必须排序的字典也是有意义的。


如果你指定了object_pairs_hook参数,通常使用的load命令将正常工作:

import json
from  collections import OrderedDict
with open('foo.json', 'r') as fp:
    metrics_types = json.load(fp, object_pairs_hook=OrderedDict)

好消息!从3.6版开始,cPython实现保留了字典的插入顺序(https://mail.python.org/pipermail/python-dev/2016-September/146327.html)。这意味着json库现在默认是有序保存的。观察python 3.5和3.6之间的行为差异。代码:

import json
data = json.loads('{"foo":1, "bar":2, "fiddle":{"bar":2, "foo":1}}')
print(json.dumps(data, indent=4))

在py3.5中,结果顺序未定义:

{
    "fiddle": {
        "bar": 2,
        "foo": 1
    },
    "bar": 2,
    "foo": 1
}

在python 3.6的cPython实现中:

{
    "foo": 1,
    "bar": 2,
    "fiddle": {
        "bar": 2,
        "foo": 1
    }
}

真正的好消息是,这已经成为python 3.7的语言规范(而不是cPython 3.6+的实现细节):https://mail.python.org/pipermail/python-dev/2017-December/151283.html

所以现在问题的答案是:升级到python 3.6!:)