Элементы в объекте JSON вышли из строя, используя «json.dumps»?

157

Я использую, 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"}
]

Я хочу иметь ключи в следующем порядке: идентификатор, имя, часовой пояс - но вместо этого у меня есть часовой пояс, идентификатор, имя.

Как мне это исправить?

Noor
источник

Ответы:

244

И 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)])
JFS
источник
2
INIT OrderedDict действительно некрасиво
жан
3
@jean: начальное значение не имеет ничего общего с OrderedDict(), вы можете передать dictв OrderedDict(), вы можете передать список упорядоченных пар , чтобы dict()тоже - хотя порядок теряется в обоих этих случаях.
Jfs
Я имею в виду, что когда вы сохраняете порядок, нужно набрать много «(» и «)»
Жан
@jean: есть ordereddict_literalsиз codetransformerпакета (альфа-качество)
JFS
25
Кроме того, если вы загрузите JSON с помощью d = json.load(f, object_pairs_hook=OrderedDict), позже json.dump(d)будет сохранен порядок исходных элементов.
Фред Янковский,
21

Как уже упоминали другие, основной диктат неупорядочен. Однако в Python есть объекты OrderedDict. (Они встроены в последние питоны, или вы можете использовать это: http://code.activestate.com/recipes/576693/ ).

Я полагаю, что новые реализации Python JSON правильно обрабатывают встроенные OrderedDicts, но я не уверен (и у меня нет легкого доступа к тестированию).

Старые реализации pythons simplejson не очень хорошо обрабатывают объекты OrderedDict ... и преобразуют их в обычные диктовки перед их выводом ... но вы можете преодолеть это, выполнив следующие действия:

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 (например, если файл находится под контролем версии или для того, чтобы читателю было проще понять, чтобы порядок входа соответствовал вашей документации.)
Майкл Андерсон
3
В этом случае вам надо просто установить sort_keysна Trueпри вызове 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)

это сработало для меня

Rabiea Ez Eldeen
источник
4

json.dump () сохранит порядок вашего словаря. Откройте файл в текстовом редакторе, и вы увидите. Он сохранит порядок независимо от того, отправляете ли вы ему OrderedDict.

Но json.load () потеряет порядок сохраненного объекта, если вы не скажете ему загрузить в OrderedDict (), что делается с параметром object_pairs_hook, как указано выше в JFSebastian.

В противном случае он потерял бы порядок, потому что при обычной операции он загружает сохраненный объект словаря в обычный dict, а обычный dict не сохраняет порядок элементов, которые ему даны.

markling
источник
На самом деле это лучшее решение, так как поддержание порядка загрузки заботится о порядке времени дампа. Спасибо за этот ответ.
Арун R
2

в JSON, как и в Javascript, порядок ключей объектов не имеет смысла, поэтому не имеет значения, в каком порядке они отображаются, это один и тот же объект.

Павел
источник
(и то же самое относится и к стандартному Python dict)
12
но поскольку JSON является строковым представлением до тех пор, пока он не будет проанализирован, для сравнения строк (например, в документах) все равно может потребоваться порядок. Поэтому я бы не сказал, что это никогда не имеет значения.
Майкл Скотт Катберт
1
Хотя это верно для стандарта Javascript (сценарий ECMA), все реализации хранят (строковые) ключи в порядке следования исходного кода.
thebjorn
1
@Paulpro действительно? который из? Я знаю, что Chrome однажды пытался следовать этому стандарту, но его отправили ( code.google.com/p/v8/issues/detail?id=164 ). Я не думал, что кто-нибудь попробует то же самое после этого ...
thebjorn
2
@paulpro вы правильно отвечаете на вопрос ОП. Однако я хочу добавить, что существуют законные способы сохранения порядка. Например, можно написать скрипт, который читает JSON, применяет некоторые преобразования и записывает результаты. Вы хотели бы сохранить порядок, чтобы инструмент сравнения четко показывал изменения.
Пол Радемахер