Tengo un Python set
que contiene objetos con__hash__
y __eq__
métodos para asegurarse de que no se incluyan duplicados en la colección.
Necesito codificar json este resultado set
, pero pasar incluso un vacío set
al json.dumps
método genera un 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
Sé que puedo crear una extensión de la json.JSONEncoder
clase que tenga un default
método personalizado , pero ni siquiera estoy seguro de por dónde empezar a convertir set
. ¿Debo crear un diccionario a partir de los set
valores dentro del método predeterminado y luego devolver la codificación? Idealmente, me gustaría hacer que el método predeterminado sea capaz de manejar todos los tipos de datos en los que el codificador original se atraganta (estoy usando Mongo como fuente de datos, por lo que las fechas también parecen generar este error)
Cualquier sugerencia en la dirección correcta sería apreciada.
EDITAR:
¡Gracias por la respuesta! Quizás debería haber sido más preciso.
Utilicé (y voté) las respuestas aquí para sortear las limitaciones de la set
traducción, pero también hay claves internas que son un problema.
Los objetos en el set
son objetos complejos que se traducen en__dict__
, pero ellos mismos también pueden contener valores para sus propiedades que podrían no ser elegibles para los tipos básicos en el codificador json.
Hay muchos tipos diferentes entrando en esto set
, y el hash básicamente calcula una identificación única para la entidad, pero en el verdadero espíritu de NoSQL no se sabe exactamente qué contiene el objeto hijo.
Un objeto puede contener un valor de fecha para starts
, mientras que otro puede tener algún otro esquema que no incluya claves que contengan objetos "no primitivos".
Es por eso que la única solución que se me ocurrió fue extender JSONEncoder
para reemplazar el default
método para activar diferentes casos, pero no estoy seguro de cómo hacerlo y la documentación es ambigua. En los objetos anidados, ¿el valor devuelto por default
ir por clave, o es solo una inclusión / descarte genérico que mira todo el objeto? ¿Cómo acomoda ese método los valores anidados? He revisado las preguntas anteriores y parece que no puedo encontrar el mejor enfoque para la codificación de casos específicos (que desafortunadamente parece ser lo que voy a necesitar hacer aquí).
fuente
dict
s? Creo que solo quieres hacer unlist
out del set y luego pasarlo al codificador ... por ejemplo:encode(list(myset))
Respuestas:
JSON notación solo tiene un puñado de tipos de datos nativos (objetos, matrices, cadenas, números, booleanos y nulos), por lo que cualquier cosa serializada en JSON debe expresarse como uno de estos tipos.
Como se muestra en los documentos del módulo json , esta conversión se puede hacer automáticamente mediante un JSONEncoder y JSONDecoder , pero luego estaría renunciando a alguna otra estructura que pueda necesitar (si convierte conjuntos en una lista, entonces pierde la capacidad de recuperar regularmente listas; si convierte conjuntos a un diccionario utilizando,
dict.fromkeys(s)
entonces pierde la capacidad de recuperar diccionarios).Una solución más sofisticada es construir un tipo personalizado que pueda coexistir con otros tipos JSON nativos. Esto le permite almacenar estructuras anidadas que incluyen listas, conjuntos, dictados, decimales, objetos de fecha y hora, etc.
Aquí hay una sesión de muestra que muestra que puede manejar listas, dictados y conjuntos:
Alternativamente, puede ser útil utilizar una técnica de serialización de propósito más general como YAML , Twisted Jelly o el módulo de pepinillos de Python . Cada uno de ellos admite una gama mucho mayor de tipos de datos.
fuente
JSONDecoder
pero no lo usaPuede crear un codificador personalizado que devuelva a
list
cuando encuentre aset
. Aquí hay un ejemplo:Puede detectar otros tipos de esta manera también. Si necesita conservar que la lista era en realidad un conjunto, podría usar una codificación personalizada. Algo como
return {'type':'set', 'list':list(obj)}
podría funcionar.Para los tipos anidados ilustrados, considere serializar esto:
Esto genera el siguiente error:
Esto indica que el codificador tomará el
list
resultado devuelto y llamará recursivamente al serializador en sus hijos. Para agregar un serializador personalizado para varios tipos, puede hacer esto:fuente
default
función volverá a llamarse, esta vez porobj
ser un objeto de fecha, por lo que solo debe probarlo y devolver una representación de fecha.Adapté solución de Raymond Hettinger a Python 3.
Esto es lo que ha cambiado:
unicode
desaparecidodefault
consuper()
base64
para serializar elbytes
tipo enstr
(porque parece quebytes
en Python 3 no se puede convertir a JSON)fuente
json.dumps()
retornos del objeto a / desde'latin1'
, omitiendo lasbase64
cosas que no son necesarias.Solo los diccionarios, las listas y los tipos de objetos primitivos (int, string, bool) están disponibles en JSON.
fuente
No necesita crear una clase de codificador personalizada para proporcionar el
default
método; se puede pasar como un argumento de palabra clave:da como resultado
[1, 2, 3]
todas las versiones compatibles de Python.fuente
Si solo necesita codificar conjuntos, no objetos generales de Python, y desea mantenerlo fácilmente legible para los humanos, se puede usar una versión simplificada de la respuesta de Raymond Hettinger:
fuente
Si solo necesita un volcado rápido y no desea implementar un codificador personalizado. Puedes usar lo siguiente:
json_string = json.dumps(data, iterable_as_array=True)
Esto convertirá todos los conjuntos (y otros iterables) en matrices. Solo tenga en cuenta que esos campos permanecerán como matrices cuando analice el json. Si desea conservar los tipos, debe escribir un codificador personalizado.
fuente
Una deficiencia de la solución aceptada es que su salida es muy específica de Python. Es decir, su salida json sin procesar no puede ser observada por un humano o cargada por otro idioma (por ejemplo, javascript). ejemplo:
Te conseguirá:
Puedo proponer una solución que rebaja el conjunto a un dict que contiene una lista al salir y vuelve a un conjunto cuando se carga en python usando el mismo codificador, preservando así la observabilidad y el agnosticismo del lenguaje:
Lo que te lleva a:
Tenga en cuenta que serializar un diccionario que tiene un elemento con una clave
"__set__"
romperá este mecanismo. Entonces se__set__
ha convertido en unadict
clave reservada . Obviamente, siéntase libre de usar otra clave más profundamente ofuscada.fuente