【问题标题】:Can I get JSON to load into an OrderedDict?我可以让 JSON 加载到 OrderedDict 中吗?
【发布时间】:2011-10-18 19:11:13
【问题描述】:

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

但它可以用作输出吗?如果有怎么办?在我的情况下,我想将 load 放入 OrderedDict,这样我就可以保持文件中键的顺序。

如果没有,是否有某种解决方法?

【问题讨论】:

  • 是的,就我而言,我正在弥合不同语言和应用程序之间的差距,并且 JSON 效果很好。但是键的顺序有点问题。如果能在json.load 中简单打勾以使用 OrderedDicts 而不是 Python 中的 Dicts,那就太棒了。
  • 这很烦人。在 Javascript(其中 json 是一个子集)中,键的顺序也不会被保留......
  • JSON 规范将对象类型定义为具有无序键...期望特定键顺序是错误的
  • 键排序通常不适用于任何类型的功能需求。它主要只是为了人类可读性。如果我只想让我的 json 打印得漂亮,我不希望任何文档顺序发生任何变化。
  • 它还有助于避免大的 git 差异!

标签: python json load ordereddictionary


【解决方案1】:

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

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

您可以将此参数传递给json.loads(如果您不需要解码器实例用于其他目的),如下所示:

>>> 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)

【讨论】:

  • 我很困惑。文档说 object_pairs_hook 会为每个解码成对的文字调用。为什么这不会为 JSON 中的每条记录创建一个新的 OrderedDict?
  • 嗯...文档的措辞有些含糊。他们的意思是“解码所有对的整个结果”将按顺序作为列表传递给object_pairs_hook,而不是“每一对都将传递给 object_pairs_hook”
  • 但是是丢失了输入json的原始顺序吗?
  • 添加 OrderedDict 钩子是否会使 dict 中的订单保持更深层次的层次结构?
  • @RandomCertainty 是的,每次在解析源代码时遇到 JSON 对象,OrderedDict 将用于构建生成的 python 值。
【解决方案2】:

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)

【讨论】:

  • 啊,但它不包括 object_pairs_hook ——这就是为什么你在 2.6 中仍然需要 simplejson。 ;)
  • 要注意simplejsonordereddict 是您需要安装的独立库。
  • 对于python 2.7+:代码中的“import json, collections”,对于python2.6-系统中的“aptitude install python-pip”和“pip install ordereddict”
  • 这比之前使用 JSONDecoder 的方法更简单、更快速。
  • 奇怪的是,在pypy中,包含的json将无法loads('{}', object_pairs_hook=OrderedDict)
【解决方案3】:

一些好消息!从 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! :)

【讨论】:

  • 虽然我在给定的示例中看到了与您相同的行为,但在 Python 3.6.4 的 CPython 实现中,json.loads('{"2": 2, "1": 1}') 对我来说变成了 {'1': 1, '2': 2}
  • @fuglede 它看起来像dict.__repr__ 对键进行排序,同时保留了基础排序。换句话说,json.loads('{"2": 2, "1": 1}').items()dict_items([('2', 2), ('1', 1)]),即使 repr(json.loads('{"2": 2, "1": 1}'))"{'1': 1, '2': 2}"
  • @SimonCharette 嗯,可能是;我实际上无法在 conda 的 pkgs/main/win-64::python-3.6.4-h0c2934d_3 中重现我自己的观察结果,所以这很难测试。
  • 但这并没有太大帮助,因为“重命名”键仍然会破坏键的顺序。
  • Python documentation link -- 文档提到“从 Python 3.7 开始,常规 dict 变成了保留顺序,因此不再需要为 JSON 生成和解析指定 collections.OrderedDict。”,其中意味着默认情况下负载以正确的顺序插入dict
【解决方案4】:

除了转储字典之外,您总是可以写出键列表,然后通过遍历列表来重建OrderedDict

【讨论】:

  • +1 用于低技术解决方案。在处理与 YAML 相同的问题时,我已经这样做了,但是必须复制有点蹩脚,尤其是当底层格式保留顺序时。避免丢失 dict 中但从键列表中丢失的键值对也可能是有意义的,在所有明确排序的项目之后附加它们。
  • 低技术解决方案还保留了在导出格式中不一定保留的上下文(IOW;有人看到 JSON,并且没有任何内容明确说明“这些键应按此顺序保留”如果他们这样做的话对其进行操作)。
  • 什么决定了“转储”的键列表的顺序是正确的?嵌套的字典呢?似乎倾销都需要处理,重建需要使用OrdereDicts递归完成。
【解决方案5】:

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

利用json 以正确的顺序转储OrderedDict 确实很好。但是,通常必须将 all JSON 字典作为 OrderedDict 读取(通过 object_pairs_hook 参数)是不必要的繁重且不一定有意义,因此 only的显式转换> 必须排序的字典也很有意义。

【讨论】:

    【解决方案6】:

    如果您指定 object_pairs_hook 参数,通常使用的加载命令将起作用:

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

    【讨论】:

      猜你喜欢
      • 2018-04-02
      • 2012-11-08
      • 1970-01-01
      • 2011-04-08
      • 2013-06-27
      • 1970-01-01
      • 1970-01-01
      • 2021-05-04
      • 2023-03-24
      相关资源
      最近更新 更多