Os itens no objeto JSON estão fora de ordem usando "json.dumps"?

157

Estou usando json.dumpspara converter em json como

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

O resultado que tenho é:

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

Quero ter as chaves na seguinte ordem: id, nome, fuso horário - mas, em vez disso, tenho fuso horário, id, nome.

Como devo corrigir isso?

Noor
fonte

Respostas:

244

O Python dict(antes do Python 3.7) e o objeto JSON são coleções não ordenadas. Você pode passar o sort_keysparâmetro para classificar as chaves:

>>> 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}'

Se você precisar de um pedido específico; você poderia usarcollections.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}'

Desde o Python 3.6 , a ordem dos argumentos das palavras-chave é preservada e as opções acima podem ser reescritas usando uma sintaxe melhor:

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

Consulte PEP 468 - Preservando a ordem dos argumentos das palavras-chave .

Se sua entrada é fornecida como JSON, para preservar o pedido (para obter OrderedDict), você pode passar object_pair_hook, conforme sugerido por @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)])
jfs
fonte
2
O init do OrderedDict é realmente feio
jean
3
@ jean: o valor inicial não tem nada a ver com OrderedDict(), você pode passar dictpara OrderedDict(), você também pode passar uma lista de pares ordenados dict()- embora a ordem seja perdida nos dois casos.
JFS
Quero dizer o init quando preservar a ordem, precisa de digitar muitos '(' e ')'
jean
@ jean: existe a ordereddict_literalspartir do codetransformerpacote (qualidade alfa)
jfs
25
Além disso, se você carregar o JSON usando d = json.load(f, object_pairs_hook=OrderedDict), um posterior json.dump(d)manterá a ordem dos elementos originais.
Fred Yankowski
21

Como outros já mencionaram, o ditado subjacente não é ordenado. No entanto, existem objetos OrderedDict em python. (Eles são construídos em pythons recentes, ou você pode usá-lo: http://code.activestate.com/recipes/576693/ ).

Acredito que as implementações mais recentes do pythons json lidem corretamente com o OrderedDicts, mas não tenho certeza (e não tenho acesso fácil para testar).

As implementações python antigas do simplejson não manipulam muito bem os objetos OrderedDict .. e os convertem em dict regulares antes de produzi-los .. mas você pode superar isso fazendo o seguinte:

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)

agora usando isso, obtemos:

>>> 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"}

O que é praticamente o desejado.

Outra alternativa seria especializar o codificador para usar diretamente sua classe de linha e, portanto, você não precisaria de nenhum ditado intermediário ou UnorderedDict.

Michael Anderson
fonte
5
Observe que os objetos JSON ainda não são ordenados ; um cliente JSON pode ler a definição do objeto e ignorar completamente a ordem das chaves e ser totalmente compatível com RFC.
Martijn Pieters
4
Martijn está correto, isso não afeta a conformidade com a RFC, mas certamente ainda pode ser valioso se você quiser ter um formato consistente para o seu JSON (por exemplo, se o arquivo estiver sob controle de versão ou para facilitar para um leitor humano comprehend, da ordem de entrada make corresponder a sua documentação).
Michael Anderson
3
Caso em que você apenas definir sort_keysa Truequando chamado json.dumps(); para a estabilidade do pedido (para teste, armazenamento em cache estável ou confirmações VCS), basta selecionar as chaves.
Martijn Pieters
7

A ordem de um dicionário não tem relação com a ordem em que foi definida. Isso é verdade para todos os dicionários, não apenas para os que foram transformados em JSON.

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

De fato, o dicionário foi "virado de cabeça para baixo" antes mesmo de chegar a json.dumps:

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

ei, eu sei que é muito tarde para esta resposta, mas adicione sort_keys e atribua false a ele da seguinte maneira:

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

isso funcionou para mim

Rabiea Ez Eldeen
fonte
4

json.dump () preservará a ordem do seu dicionário. Abra o arquivo em um editor de texto e você verá. Ele preservará o pedido, independentemente de você enviar um OrderedDict.

Mas json.load () perderá a ordem do objeto salvo, a menos que você solicite que ele carregue em um OrderedDict (), o que é feito com o parâmetro object_pairs_hook como o JFSebastian instruiu acima.

Caso contrário, ele perderia a ordem porque, sob operação usual, carrega o objeto de dicionário salvo em um ditado regular e um ditado regular não preserva a aparência dos itens fornecidos.

marcação
fonte
Essa é, na verdade, uma solução melhor, pois a manutenção do pedido sob carga cuida da solicitação do tempo de despejo. Obrigado por esta resposta.
Arun R
2

em JSON, como em Javascript, a ordem das chaves do objeto não faz sentido; portanto, não importa em que ordem elas são exibidas, é o mesmo objeto.

Paulo
fonte
(e o mesmo também é válido para um Python padrão dict)
12
mas como JSON é uma representação de sequência até ser analisada, as comparações de sequência (como em documentos) ainda podem exigir ordem. Então, eu não diria que isso nunca importa.
Michael Scott Cuthbert
1
Embora isso seja verdade no padrão Javascript (script ECMA), todas as implementações mantêm as chaves (string) na ordem de origem.
#
1
@Paulpro realmente? qual? Sei que o Chrome tentou seguir o padrão aqui uma vez, mas foi submetido a submissão ( code.google.com/p/v8/issues/detail?id=164 ). Eu não acho que ninguém iria tentar a mesma coisa depois disso ...
thebjorn
2
@paulpro, você está respondendo corretamente à pergunta do OP. Quero acrescentar, no entanto, que existem usos legítimos para preservar a ordem. Por exemplo, pode-se escrever um script que leia JSON, aplique alguma transformação e escreva de volta os resultados. Você deseja que a ordem seja preservada para que uma ferramenta diff mostre claramente as alterações.
Paul Rademacher 11/06