Can I get JSON to load into an OrderedDict?
好的,我可以在json.dump中使用ordereddict。也就是说,ordereddict可以用作JSON的输入。
但它能被用作输出吗?如果是这样怎么办?在我的例子中,我想将cx1〔1〕转换成一个有序的ICT,这样我就可以将密钥的顺序保存在文件中。
如果没有,有什么解决办法吗?
- 从来没有试图维持秩序,尽管我可以肯定地看到它是如何有用的。
- 是的,在我的例子中,我正在弥合不同语言和应用程序之间的鸿沟,JSON工作得很好。但是钥匙的顺序有点问题。如果能在json.load中简单地打勾来使用orderedticts而不是python中的dict,那就太棒了。
- 这很烦人。在javascript中(JSON是其中的一个子集),键的顺序也不被保留…
- JSON规范将对象类型定义为具有无序键…期待特定的关键订单是错误的
- 密钥排序通常不适用于任何类型的功能需求。它主要是为了人类的可读性。如果我只想让我的JSON得到完美的打印,我完全不希望任何文档顺序发生更改。
- 它还有助于避免较大的Git差异!
是的,你可以。通过向jsondecoder指定object_pairs_hook参数。事实上,这是文档中给出的确切示例。
1 2 3
| >>> json.JSONDecoder(object_pairs_hook=collections.OrderedDict).decode('{"foo":1,"bar": 2}')
OrderedDict([('foo', 1), ('bar', 2)])
>>> |
您可以将此参数传递给json.loads(如果您不需要其他用途的解码器实例),如下所示:
1 2 3 4 5 6 7 8 9
| >>> 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的方式相同:
1
| >>> data = json.load(open('config.json'), object_pairs_hook=OrderedDict) |
- 我很困惑。文档说,对象对钩子被调用,每个文本被解码成对。为什么不为JSON中的每个记录创建一个新的ordereddict?
- 隐马尔可夫模型。。。这些文件的措辞有些含糊不清。它们的意思是"解码所有对的全部结果"将按顺序传递给object_pairs_hook,而不是"每对都将传递给对象对挂钩",
- 但是否丢失了输入json的原始顺序?
- 很惊讶地看到json.load没有默认地保持它的顺序,但看起来它只是反映了json本身的功能——{}是无序的,但json中的[]的顺序如本文所述。
python 2.7的简单版本+
1
| my_ordered_dict = json.loads(json_str, object_pairs_hook=collections.OrderedDict) |
或者对于python 2.4到2.6
1 2 3 4
| import simplejson as json
import ordereddict
my_ordered_dict = json.loads(json_str, object_pairs_hook=ordereddict.OrderedDict) |
- Python2.6附带了JSON。
- 啊,但它不包括对象对挂钩——这就是为什么在2.6中仍然需要simplejson。;)
- 需要注意的是,simplejson和ordereddict是需要安装的独立库。
- 对于python 2.7+,代码中为"import json,collections",对于python2.6,系统中为"aptitude install python pip"和"pip install ordereddict"。
- 与使用JSondecoder的前一种方法相比,这种方法更容易、更快速。
- 奇怪的是,在Pypy中,包含的JSON将无法用于loads('{}', object_pairs_hook=OrderedDict)。
- @mjhm我收到typeerror的任何原因:"ordereddict"对象不可调用?
- @迈克——不是阿飞,虽然我很久没看过这个了。
- @迈克-也许你在增加一对括号?应该是object_pairs_hook=OrderedDict,不是object_pairs_hook=OrderedDict()。
- FYI CentOS-6有一个旧版本的python simplejson v2.0.9,它没有所需的"对象对挂钩"。所以一个解决方法是使用排序字典对象对collections.OrderedDict(json.loads(json.dumps(x.items())))的json.dumps。
- 但是否丢失了输入json的原始顺序?
好消息!自3.6版以来,cpython实现保留了字典的插入顺序(https://mail.python.org/pipermail/python dev/2016-09/146327.html)。这意味着JSON库现在默认为订单保留。观察python 3.5和3.6之间的行为差异。代码:
1 2 3
| import json
data = json.loads('{"foo":1,"bar":2,"fiddle":{"bar":2,"foo":1}}')
print(json.dumps(data, indent=4)) |
在PY3.5中,结果顺序未定义:
1 2 3 4 5 6 7 8
| {
"fiddle": {
"bar": 2,
"foo": 1
},
"bar": 2,
"foo": 1
} |
在python 3.6的cpython实现中:
1 2 3 4 5 6 7 8
| {
"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!:)
- 这是好消息!
- 虽然我在给出的示例中看到了与您相同的行为,但在python 3.6.4的cpython实现中,json.loads('{"2": 2,"1": 1}')对我来说变成了{'1': 1, '2': 2}。
- @fuglede看起来像是dict.__repr__在保留底层顺序的同时对键进行排序。换言之,即使repr(json.loads('{"2": 2,"1": 1}'))是"{'1': 1, '2': 2}",json.loads('{"2": 2,"1": 1}').items()也是dict_items([('2', 2), ('1', 1)])。
- @SimonSharette-hm,可能是的;我实际上无法在Conda的pkgs/main/win-64::python-3.6.4-h0c2934d-3中重现我自己的观察结果,所以这将很难测试。
除了转储dict之外,您还可以编写键列表,然后通过遍历该列表来重新构造OrderedDict?
- +低技术解决方案。我在处理与yaml相同的问题时已经做到了这一点,但是必须复制有点差劲,特别是当底层格式保留顺序时。也可以避免丢失dict中的键值对,但这些键值对在所有显式排序的项之后都被附加到键值列表中。
- 低技术解决方案还保留了不必以导出格式保存的上下文(iow;有人看到JSON,但没有明确说明"如果对其进行操作,这些键应保持此顺序"的内容)。
- 什么决定了"转储"键列表的顺序正确?嵌套式听写怎么样?似乎这两种情况都需要处理,重建需要使用OrdereDicts递归进行。
除了在字典旁边转储键的有序列表外,另一个技术含量低的解决方案,其优点是可以明确地转储键值对ordered_dict.items()的(有序)列表;加载是一个简单的OrderedDict()
。尽管JSON没有这个概念(JSON字典没有顺序),但它处理有序字典。
利用json以正确的顺序转储所订购的ICT这一事实确实很好。但是,一般来说,将所有JSON字典作为一个有序的ICT(通过object_pairs_hook参数)读取是不必要的,也不一定有意义的,因此仅对必须排序的字典进行显式转换也是有意义的。
如果指定对象对挂钩参数,通常使用的加载命令将工作:
1 2 3 4
| import json
from collections import OrderedDict
with open('foo.json', 'r') as fp:
metrics_types = json.load(fp, object_pairs_hook=OrderedDict) |