我能得到JSON加载到OrderedDict吗?

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

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

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

166889 次浏览

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

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

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

你可以像这样将这个参数传递给json.loads(如果你不需要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.load的方法是一样的:

>>> 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();loading是一个简单的OrderedDict(<list of key-value pairs>)。这处理了一个有序字典,尽管JSON没有这个概念(JSON字典没有顺序)。

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

如果指定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!:)