Елементи в об'єкті 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, ім'я.

Як я можу це виправити?

Відповіді:


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
Ініціювати OrdenDict по-справжньому потворно
jean

3
@jean: початкове значення не має нічого спільного з OrderedDict(), ви можете передати dictв OrderedDict(), ви можете передати список упорядкованих пар , щоб dict()теж - хоча порядок втрачається в обох цих випадках.
jfs

Я маю на увазі його init, коли зберігати порядок, потрібно набрати багато '(' і ')'
jean

@jean: є ordereddict_literalsз codetransformerпакету (альфа-якість)
jfs

25
Крім того, якщо ви завантажуєте JSON за допомогою d = json.load(f, object_pairs_hook=OrderedDict), пізніше json.dump(d)збереже порядок початкових елементів.
Фред Янковський

21

Як згадували інші, основний дикт є не упорядкованим. Однак в python є об'єкти OrdersDict. (Вони вбудовані в останні пітони, або ви можете використовувати це: http://code.activestate.com/recipes/576693/ ).

Я вважаю, що новіші реалізації пітонів json правильно поводяться з вбудованими в OrdersDicts, але я не впевнений (і у мене немає простого доступу до тесту).

Старі реалізації pythons simplejson не дуже добре обробляють об'єкти OrdersDict .. і перетворюють їх у звичайні дикти, перш ніж виводити їх .. але ви можете подолати це, виконавши наступне:

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

Що в значній мірі бажано.

Іншою альтернативою може бути спеціалізація кодера для прямого використання класу рядків, і тоді вам не знадобиться проміжний дікт або UnorderedDict.


5
Зауважте, що об'єкти JSON досі не упорядковані ; клієнт JSON може прочитати визначення об'єкта і повністю проігнорувати порядок клавіш і бути повністю сумісним з RFC.
Martijn Pieters

4
Мартийн правильно, це не впливає на відповідність RFC, але воно, безумовно, може бути цінним, якщо ви хочете мати послідовний формат для свого JSON (Наприклад, якщо файл знаходиться під контролем версій, або щоб полегшити читачеві людину Зрозумійте, про те, щоб наказ про вхід відповідав вашій документації.)
Майкл Андерсон

3
У такому випадку ви просто налаштовуєтесь sort_keysпід Trueчас дзвінка json.dumps(); для стабільності замовлення (для тестування, стабільного кешування чи VCS), клавіш сортування достатньо.
Martijn Pieters

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 і призначи неправдивим так:

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

це працювало для мене


4

json.dump () збереже порядок вашого словника. Відкрийте файл у текстовому редакторі, і ви побачите. Він збереже замовлення незалежно від того, надіслали ви його OrdersDict.

Але json.load () втратить порядок збереженого об'єкта, якщо ви не скажете його завантажувати в OrdersDict (), що робиться з параметром object_pairs_hook, як описано вище JFSebastian.

В іншому випадку він втратить порядок, оскільки за звичайної операції він завантажує збережений словниковий об'єкт у звичайний дикт, а звичайний дикт не зберігає одержання йому заданих елементів.


Це насправді є кращим виправленням, оскільки підтримка порядку завантаження забезпечує порядок скидання часу. Дякую за цю відповідь.
Arun R

2

в JSON, як і в Javascript, порядок клавіш об'єктів безглуздий, тому насправді не має значення, в якому порядку вони відображаються, це той самий об’єкт.


(те ж саме стосується і стандартного Python dict)

12
але оскільки JSON - це рядкове представлення до його розбору, порівняння рядків (наприклад, у doctests) все ще може вимагати порядку. Тому я б не сказав, що це ніколи не має значення.
Майкл Скотт Катберт

1
Хоча це стосується стандарту Javascript (сценарій ECMA), всі реалізації зберігають (рядкові) клавіші у вихідному порядку.
thebjorn

1
@Paulpro дійсно? який? Я знаю, що Chrome намагався дотримуватися стандарту тут один раз, але його було подано ( code.google.com/p/v8/isissue/detail?id=164 ). Я не думав, що хтось спробує те ж саме після цього ...
thebjorn

2
@paulpro ви правильно вирішуєте питання ОП. Хочеться додати, що для збереження порядку існують законні можливості. Наприклад, можна написати сценарій, який читає JSON, застосовує певну трансформацію та записує результати. Ви хочете зберегти порядок, щоб різний інструмент чітко відображав зміни.
Пол Радемахер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.