У меня есть Python, set
который содержит объекты __hash__
и __eq__
методы, чтобы убедиться, что дубликаты не включены в коллекцию.
Мне нужно JSon закодировать этот результат set
, но проходя даже пустой set
в json.dumps
метод поднимает TypeError
.
File "/usr/lib/python2.7/json/encoder.py", line 201, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python2.7/json/encoder.py", line 264, in iterencode
return _iterencode(o, 0)
File "/usr/lib/python2.7/json/encoder.py", line 178, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: set([]) is not JSON serializable
Я знаю, что могу создать расширение для json.JSONEncoder
класса, у которого есть собственный default
метод, но я даже не уверен, с чего начать преобразование через set
. Должен ли я создать словарь из set
значений в методе по умолчанию, а затем вернуть кодировку для этого? В идеале я хотел бы сделать метод по умолчанию способным обрабатывать все типы данных, которые задыхается в исходном кодере (я использую Mongo в качестве источника данных, поэтому даты, похоже, тоже вызывают эту ошибку)
Любой намек в правильном направлении будет оценен.
РЕДАКТИРОВАТЬ:
Спасибо за ответ! Возможно, мне следовало быть более точным.
Я использовал (и проголосовал) ответы здесь, чтобы обойти ограничения set
переводимого, но есть и внутренние ключи, которые также являются проблемой.
Объекты в set
являются сложными объектами, которые преобразуются __dict__
, но сами они также могут содержать значения своих свойств, которые могут не подходить для базовых типов в кодировщике json.
В него входит много разных типов set
, и хеш в основном вычисляет уникальный идентификатор для сущности, но в истинном духе NoSQL точно не сказано, что содержит дочерний объект.
Один объект может содержать значение даты для starts
, тогда как другой может иметь некоторую другую схему, которая не содержит ключей, содержащих «не примитивные» объекты.
Вот почему единственное решение, которое я мог придумать, - это расширение метода JSONEncoder
замены default
для включения разных случаев, но я не уверен, как это сделать, и документация неоднозначна. Во вложенных объектах, значение, возвращаемое при default
переходе по ключу, или это просто общее включение / отбрасывание, которое смотрит на весь объект? Как этот метод учитывает вложенные значения? Я просмотрел предыдущие вопросы и, похоже, не могу найти лучший подход к кодированию для конкретного случая (что, к сожалению, похоже на то, что мне нужно сделать здесь).
источник
dict
с? Я думаю, что вы хотите сделатьlist
из набора только что, а затем передать его кодировщику ... например:encode(list(myset))
Ответы:
Нотация JSON имеет только несколько собственных типов данных (объекты, массивы, строки, числа, логические значения и ноль), поэтому все, что сериализовано в JSON, должно быть выражено как один из этих типов.
Как показано в документации по json-модулю , это преобразование может выполняться автоматически JSONEncoder и JSONDecoder , но тогда вы отказываетесь от какой-то другой структуры, которая может вам понадобиться (если вы преобразуете наборы в список, вы теряете возможность регулярно восстанавливать списки; если вы преобразуете наборы в словарь, используя его,
dict.fromkeys(s)
вы теряете возможность восстанавливать словари).Более сложным решением является создание пользовательского типа, который может сосуществовать с другими собственными типами JSON. Это позволяет хранить вложенные структуры, которые включают списки, наборы, дикты, десятичные дроби, объекты даты и времени и т. Д .:
Вот пример сеанса, показывающий, что он может обрабатывать списки, запросы и наборы:
В качестве альтернативы может быть полезно использовать более общую технику сериализации, такую как YAML , Twisted Jelly или модуль засолки Python . Каждый из них поддерживает гораздо больший диапазон типов данных.
источник
JSONDecoder
но не использует егоВы можете создать собственный кодировщик, который возвращает,
list
когда встречаетset
. Вот пример:Вы также можете обнаружить другие типы таким же образом. Если вам нужно сохранить, что список на самом деле был набором, вы можете использовать пользовательскую кодировку. Нечто подобное
return {'type':'set', 'list':list(obj)}
может сработать.Чтобы проиллюстрировать вложенные типы, рассмотрите сериализацию этого:
Это вызывает следующую ошибку:
Это указывает на то, что кодер примет
list
возвращенный результат и рекурсивно вызовет сериализатор для его дочерних элементов. Чтобы добавить настраиваемый сериализатор для нескольких типов, вы можете сделать это:источник
default
функция будет вызвана снова, на этот раз сobj
использованием объекта даты, поэтому вам просто нужно проверить ее и вернуть представление даты.Я адаптировал решение Raymond Hettinger для Python 3.
Вот что изменилось:
unicode
исчезнувшийdefault
сsuper()
base64
для сериализацииbytes
типа вstr
(потому что кажется, чтоbytes
в Python 3 не может быть преобразован в JSON)источник
json.dumps()
возвращаемого в / из'latin1'
, пропуская ненужныеbase64
вещи.В словаре JSON доступны только словари, списки и типы примитивных объектов (int, string, bool).
источник
Вам не нужно создавать собственный класс кодировщика для предоставления
default
метода - его можно передать в качестве аргумента ключевого слова:результаты во
[1, 2, 3]
всех поддерживаемых версиях Python.источник
Если вам нужно только кодировать наборы, а не общие объекты Python, и вы хотите, чтобы он был легко читаемым человеком, можно использовать упрощенную версию ответа Раймонда Хеттингера:
источник
Если вам нужен просто быстрый дамп и вы не хотите реализовывать пользовательский кодировщик. Вы можете использовать следующее:
json_string = json.dumps(data, iterable_as_array=True)
Это преобразует все наборы (и другие итерируемые элементы) в массивы. Просто помните, что эти поля останутся массивами, когда вы проанализируете JSON. Если вы хотите сохранить типы, вам нужно написать собственный кодировщик.
источник
Одним из недостатков принятого решения является то, что его вывод очень специфичен для Python. Т.е. его необработанный вывод json не может наблюдаться человеком или загружаться другим языком (например, javascript). пример:
Вы получите:
Я могу предложить решение, которое понижает набор до dict, содержащего список при выходе, и возвращается к набору при загрузке в python с использованием того же кодера, сохраняя тем самым наблюдаемость и независимость от языка:
Который получает вас:
Обратите внимание, что сериализация словаря, который имеет элемент с ключом
"__set__"
, сломает этот механизм. Так__set__
что теперь стало зарезервированнымdict
ключом. Очевидно, вы можете использовать другой, более глубоко запутанный ключ.источник