JSON对象中的项目使用“ json.dumps”乱序了吗?


156

json.dumps用来转换成json之类的

countries.append({"id":row.id,"name":row.name,"timezone":row.timezone})
print json.dumps(countries)

我得到的结果是:

[
   {"timezone": 4, "id": 1, "name": "Mauritius"}, 
   {"timezone": 2, "id": 2, "name": "France"}, 
   {"timezone": 1, "id": 3, "name": "England"}, 
   {"timezone": -4, "id": 4, "name": "USA"}
]

我想按以下顺序获取键:ID,名称,时区-但我却拥有时区,ID,名称。

我该如何解决?

Answers:


243

Python dict(在Python 3.7之前)和JSON对象都是无序集合。您可以传递sort_keys参数来对键进行排序:

>>> import json
>>> json.dumps({'a': 1, 'b': 2})
'{"b": 2, "a": 1}'
>>> json.dumps({'a': 1, 'b': 2}, sort_keys=True)
'{"a": 1, "b": 2}'

如果您需要特定的订单;您可以使用collections.OrderedDict

>>> from collections import OrderedDict
>>> json.dumps(OrderedDict([("a", 1), ("b", 2)]))
'{"a": 1, "b": 2}'
>>> json.dumps(OrderedDict([("b", 2), ("a", 1)]))
'{"b": 2, "a": 1}'

从Python 3.6开始,关键字参数顺序得以保留,并且可以使用更好的语法重写以上内容:

>>> json.dumps(OrderedDict(a=1, b=2))
'{"a": 1, "b": 2}'
>>> json.dumps(OrderedDict(b=2, a=1))
'{"b": 2, "a": 1}'

请参阅PEP 468 –保留关键字参数顺序

如果你的输入给出JSON然后保存顺序(得到OrderedDict),你可以传递object_pair_hook通过@Fred Yankowski的建议

>>> json.loads('{"a": 1, "b": 2}', object_pairs_hook=OrderedDict)
OrderedDict([('a', 1), ('b', 2)])
>>> json.loads('{"b": 2, "a": 1}', object_pairs_hook=OrderedDict)
OrderedDict([('b', 2), ('a', 1)])

2
OrderedDict的初始化非常丑陋
让·吉恩(

3
@jean:初始值与无关OrderedDict(),您可以将传递dictOrderedDict(),也可以将有序对的列表也传递给dict()-尽管在这两种情况下都丢失了顺序。
jfs 2015年

我的意思是在保留订单时将其初始化,需要键入许多'('和')'
jean


25
此外,如果您使用加载JSON d = json.load(f, object_pairs_hook=OrderedDict),则更高版本json.dump(d)将保留原始元素的顺序。
Fred Yankowski

21

正如其他人提到的那样,基本的命令是无序的。但是在python中有OrderedDict对象。(它们内置在最新的python中,或者您可以使用此代码:http : //code.activestate.com/recipes/576693/)。

我相信较新的pythons json实现可以正确处理内置的OrderedDicts,但是我不确定(而且我无法轻松访问测试)。

旧的python simplejson实现无法很好地处理OrderedDict对象..并在输出它们之前将它们转换为常规dict ..但您可以通过执行以下操作来克服这一点:

class OrderedJsonEncoder( simplejson.JSONEncoder ):
   def encode(self,o):
      if isinstance(o,OrderedDict.OrderedDict):
         return "{" + ",".join( [ self.encode(k)+":"+self.encode(v) for (k,v) in o.iteritems() ] ) + "}"
      else:
         return simplejson.JSONEncoder.encode(self, o)

现在使用这个我们得到:

>>> import OrderedDict
>>> unordered={"id":123,"name":"a_name","timezone":"tz"}
>>> ordered = OrderedDict.OrderedDict( [("id",123), ("name","a_name"), ("timezone","tz")] )
>>> e = OrderedJsonEncoder()
>>> print e.encode( unordered )
{"timezone": "tz", "id": 123, "name": "a_name"}
>>> print e.encode( ordered )
{"id":123,"name":"a_name","timezone":"tz"}

这几乎是所需要的。

另一种选择是专门使编码器直接使用您的行类,这样就不需要任何中间dict或UnorderedDict。


5
注意JSON对象仍然无序的;JSON客户端可以读取对象定义,并且完全忽略键的顺序,并且完全符合RFC。
马丁·彼得斯

4
Martijn是正确的,这不会影响RFC遵从性,但是如果您希望为JSON保持一致的格式(例如,如果文件受版本控制,或者使人类读者更容易理解),它无疑仍然很有价值。理解,以使进货顺序与您的文档相匹配。)
Michael Anderson

3
在这种情况下,您只需设置sort_keysTrue通话json.dumps(); 对于订单稳定性(用于测试,稳定的缓存或VCS提交),排序键就足够了。
马丁·彼得斯

7

字典的顺序与其定义的顺序没有任何关系。这对所有字典都是正确的,不仅是那些变成JSON的字典。

>>> {"b": 1, "a": 2}
{'a': 2, 'b': 1}

实际上,该字典甚至在到达之前就被“颠倒了” json.dumps

>>> {"id":1,"name":"David","timezone":3}
{'timezone': 3, 'id': 1, 'name': 'David'}

6

嘿,我知道这个答案太迟了,但是添加sort_keys并为它赋false,如下所示:

json.dumps({'****': ***},sort_keys=False)

这对我有用


4

json.dump()将保留字典的序号。在文本编辑器中打开文件,您将看到。无论您是否发送OrderedDict,它都会保留订单。

但是json.load()会丢失已保存对象的顺序,除非您告诉它加载到OrderedDict()中,这是通过上述JFSebastian的object_pairs_hook参数完成的。

否则,它将失去顺序,因为在常规操作下,它将保存的字典对象加载到常规字典中,而常规字典不保留给出的项目的价格。


实际上,这是一个更好的解决方案,因为保持加载顺序可以处理转储时间排序。感谢您的回答。
阿伦R

2

在JSON中,就像在Javascript中一样,对象键的顺序是没有意义的,因此它们按什么顺序显示实际上并不重要,它是同一对象。


(标准Python也是如此dict

12
但是由于JSON在解析之前都是字符串表示形式,因此字符串比较(例如在doctests中)可能仍然需要排序。所以我不会说这没关系。
Michael Scott Cuthbert 2014年

1
尽管Javascript(ECMA脚本)标准确实如此,但所有实现按源顺序保留(字符串)键。
thebjorn 2014年

1
@Paulpro真的吗?哪一个?我知道Chrome浏览器曾经尝试过遵循此标准,但遭到了提交(code.google.com/p/v8/issues/detail?id=164)的抨击。在那之后,我认为没有人会尝试同样的事情……
thebjorn 2014年

2
@paulpro您正在正确解决OP的问题。我想补充一点,但是,保留秩序有合法的用途。例如,可能编写了一个脚本,该脚本读取JSON,应用一些转换并写回结果。您需要保留订单,以便差异工具可以清楚地显示更改。
Paul Rademacher
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.