Могу ли я получить JSON для загрузки в OrderedDict?

428

Итак, я могу использовать OrderedDict в json.dump. То есть OrderedDict может использоваться в качестве входных данных для JSON.

Но можно ли это использовать в качестве выхода? Если так, то как? В моем случае я бы хотел loadиспользовать OrderedDict, чтобы сохранить порядок ключей в файле.

Если нет, есть ли какое-то решение?

c00kiemonster
источник
Никогда не пытался поддерживать порядок, хотя я точно вижу, как это будет полезно.
Feathj
1
Да, в моем случае я сокращаю разрыв между различными языками и приложениями, и JSON работает очень хорошо. Но порядок ключей является проблемой. Было бы здорово иметь простую галочку, json.loadчтобы использовать OrderedDicts вместо Dicts в Python.
c00kiemonster
3
Спецификация JSON определяет тип объекта как имеющий неупорядоченные ключи ... ожидание определенного порядка ключей является ошибкой
Anentropic
3
Порядок ключей обычно не соответствует функциональным требованиям. Это в основном только для удобства чтения. Если я просто хочу, чтобы мой json был хорошо напечатан, я не ожидаю, что какой-либо порядок документов изменится вообще.
Соленья
5
Это также помогает избежать больших Git Diff!
Ричард Раст

Ответы:

610

Да, ты можешь. Указав object_pairs_hookаргумент JSONDecoder . Фактически, это точный пример, приведенный в документации.

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

Вы можете передать этот параметр json.loads(если вам не нужен экземпляр Decoder для других целей) следующим образом:

>>> 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)
SingleNegationElimination
источник
3
Я в замешательстве. В документах говорится, что object_pairs_hook вызывается для каждого литерала, который декодируется в пары. Почему это не создает новый OrderedDict для каждой записи в JSON?
Тим Китинг
3
Хм ... документы несколько двусмысленно сформулированы. То, что они имеют в виду, что «весь результат декодирования всех пар» будет передаваться в порядке списка object_pairs_hook, а не «каждая пара будет передаваться в object_pairs_hook»,
SingleNegationElimination
Но теряет ли первоначальный порядок ввода json?
SIslam
Был удивлен , чтобы увидеть , что json.loadне держит это упорядоченный по умолчанию, но похоже , что это только отражение того, что само по себе делает JSon - {}неупорядоченны, но []в формате JSON будут заказаны , как описано здесь
кардамон
1
@RandomCertainty yes, каждый раз, когда объект JSON встречается во время синтаксического анализа источника, OrderedDictбудет использоваться для создания результирующего значения python.
SingleNegationElimination
125

Простая версия для 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)
mjhm
источник
4
Аааа, но он не включает в себя object_pairs_hook - вот почему вам все еще нужен простой json в 2.6. ;)
mjhm
8
Хочу отметить, что simplejsonи ordereddictэто отдельные библиотеки, которые нужно установить.
phunehehe
2
для python 2.7+: «импорт json, коллекций» в коде, для python2.6 - «aptitude install python-pip» и «pip install orderdict» в системе
ZiTAL
Это намного проще и быстрее, чем предыдущий метод с JSONDecoder.
Натим
Как ни странно, в pypy включенный json не сможет loads('{}', object_pairs_hook=OrderedDict).
Мэтью Шинкель
37

Несколько хороших новостей! Начиная с версии 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-De .html

Итак, ответ на ваш вопрос теперь таков: обновитесь до python 3.6! :)

pelson
источник
1
Хотя я вижу то же поведение, что и вы в данном примере, в CPython реализация Python 3.6.4 json.loads('{"2": 2, "1": 1}')становится {'1': 1, '2': 2}для меня.
fuglede
1
@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 Хм, может быть; На самом деле я не могу воспроизвести свое собственное наблюдение в pkgs / main / win-64 :: python-3.6.4-h0c2934d_3 Конды, так что это будет сложно проверить.
Fuglede
Это не очень помогает, так как «переименование» ключей по-прежнему нарушает порядок ключей.
Хабро
7

Вы всегда можете выписать список ключей в дополнение к выводу dict, а затем восстановить его OrderedDict, перебирая список?

янтарный
источник
1
+1 за низкотехнологичное решение. Я сделал это, когда имел дело с той же проблемой с YAML, но необходимость дублировать это немного неубедительно, особенно когда основной формат сохраняет порядок. Может также иметь смысл избегать потери пар ключ-значение, которые есть в dict, но отсутствуют в списке ключей, добавляя их после всех явно упорядоченных элементов.
Mu Mind
2
Низкотехнологичное решение также сохраняет контекст, который не обязательно сохраняется в экспортированном формате (IOW; кто-то видит JSON, и там нет ничего явно заявляющего, что «эти ключи должны оставаться в таком порядке», если они выполняют с ним манипуляции).
Amber
Что определяет, что список «сброшенных» ключей находится в правильном порядке? Как насчет вложенных диктов? Похоже, что и дамп должен был бы справиться с этим, и реконструкция должна была бы быть сделана рекурсивно, используя OrdereDicts.
Мартино
5

В дополнение к выводу упорядоченного списка ключей вместе со словарем, другое низкотехнологичное решение, которое имеет явное преимущество, заключается в выводе (упорядоченного) списка пар ключ-значение ordered_dict.items(); загрузка простаOrderedDict(<list of key-value pairs>) . Это обрабатывает упорядоченный словарь, несмотря на тот факт, что JSON не имеет этой концепции (словари JSON не имеют порядка).

Это действительно приятно использовать тот факт, что jsonдамп OrderedDict в правильном порядке. Однако в общем случае неоправданно тяжело и необязательно иметь смысл читать все словари JSON как OrderedDict (через object_pairs_hookаргумент), поэтому имеет смысл также явное преобразование только тех словарей, которые должны быть упорядочены.

Эрик О Лебиго
источник
4

Обычно используемая команда загрузки будет работать, если вы укажете параметр 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)
NTG
источник