J'ai un Python set
qui contient des objets avec __hash__
et des __eq__
méthodes afin de m'assurer qu'aucun doublon n'est inclus dans la collection.
J'ai besoin de json encoder ce résultat set
, mais passer même un vide set
à la json.dumps
méthode lève 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
Je sais que je peux créer une extension pour la json.JSONEncoder
classe qui a une default
méthode personnalisée , mais je ne sais même pas par où commencer la conversion via le set
. Dois-je créer un dictionnaire à partir des set
valeurs de la méthode par défaut, puis renvoyer l'encodage à ce sujet? Idéalement, j'aimerais que la méthode par défaut soit capable de gérer tous les types de données sur lesquels l'encodeur d'origine s'étouffe (j'utilise Mongo comme source de données, donc les dates semblent également soulever cette erreur)
Tout indice dans la bonne direction serait apprécié.
ÉDITER:
Merci d'avoir répondu! J'aurais peut-être dû être plus précis.
J'ai utilisé (et voté) les réponses ici pour contourner les limites de la set
traduction, mais il y a aussi des clés internes qui posent un problème.
Les objets dans le set
sont des objets complexes qui se traduisent en __dict__
, mais ils peuvent également contenir des valeurs pour leurs propriétés qui pourraient ne pas être éligibles pour les types de base dans l'encodeur json.
Il y a beaucoup de types différents qui entrent en jeu set
, et le hachage calcule essentiellement un identifiant unique pour l'entité, mais dans le véritable esprit de NoSQL, il est impossible de dire exactement ce que contient l'objet enfant.
Un objet peut contenir une valeur de date pour starts
, tandis qu'un autre peut avoir un autre schéma qui n'inclut aucune clé contenant des objets "non primitifs".
C'est pourquoi la seule solution à laquelle je pouvais penser était d'étendre le JSONEncoder
pour remplacer la default
méthode pour activer différents cas - mais je ne sais pas comment s'y prendre et la documentation est ambiguë. Dans les objets imbriqués, la valeur renvoyée par default
passe-t-elle par clé, ou s'agit-il simplement d'une inclusion / suppression générique qui regarde l'objet entier? Comment cette méthode prend-elle en charge les valeurs imbriquées? J'ai parcouru les questions précédentes et je n'arrive pas à trouver la meilleure approche pour l'encodage spécifique au cas (ce qui semble malheureusement être ce que je vais devoir faire ici).
la source
dict
s? Je pense que vous voulez juste faire unlist
hors de l'ensemble et ensuite le passer à l'encodeur ... par exemple:encode(list(myset))
Réponses:
La notation JSON n'a qu'une poignée de types de données natifs (objets, tableaux, chaînes, nombres, booléens et null), donc tout ce qui est sérialisé dans JSON doit être exprimé comme l'un de ces types.
Comme indiqué dans la documentation du module json , cette conversion peut être effectuée automatiquement par un JSONEncoder et JSONDecoder , mais vous abandonneriez alors une autre structure dont vous pourriez avoir besoin (si vous convertissez des ensembles en liste, vous perdez la possibilité de récupérer des listes; si vous convertissez des ensembles en dictionnaire en utilisant,
dict.fromkeys(s)
vous perdez la possibilité de récupérer des dictionnaires)Une solution plus sophistiquée consiste à créer un type personnalisé qui peut coexister avec d'autres types JSON natifs. Cela vous permet de stocker des structures imbriquées qui incluent des listes, des ensembles, des dictées, des décimales, des objets datetime, etc.:
Voici un exemple de session montrant qu'il peut gérer des listes, des dictionnaires et des ensembles:
Alternativement, il peut être utile d'utiliser une technique de sérialisation plus générale telle que YAML , Twisted Jelly ou le module pickle de Python . Celles-ci prennent chacune en charge une gamme beaucoup plus étendue de types de données.
la source
JSONDecoder
mais ne l'utilise pasVous pouvez créer un encodeur personnalisé qui renvoie a
list
lorsqu'il rencontre unset
. Voici un exemple:Vous pouvez également détecter d'autres types de cette façon. Si vous devez conserver que la liste était en fait un ensemble, vous pouvez utiliser un encodage personnalisé. Quelque chose comme ça
return {'type':'set', 'list':list(obj)}
pourrait fonctionner.Pour illustrer les types imbriqués, envisagez de sérialiser ceci:
Cela soulève l'erreur suivante:
Cela indique que l'encodeur prendra le
list
résultat retourné et appellera récursivement le sérialiseur sur ses enfants. Pour ajouter un sérialiseur personnalisé pour plusieurs types, vous pouvez le faire:la source
default
fonction sera appelée à nouveau, cette foisobj
étant un objet date, il vous suffit donc de la tester et de renvoyer une représentation de date.J'ai adapté la solution de Raymond Hettinger à python 3.
Voici ce qui a changé:
unicode
disparudefault
avecsuper()
base64
pour sérialiser lebytes
type enstr
(car il semble qu'enbytes
python 3 ne puisse pas être converti en JSON)la source
json.dumps()
retours d' objets octets vers / depuis'latin1'
, en sautant lesbase64
choses qui ne sont pas nécessaires.Seuls les dictionnaires, listes et types d'objets primitifs (int, string, bool) sont disponibles en JSON.
la source
Vous n'avez pas besoin de créer une classe d'encodeur personnalisée pour fournir la
default
méthode - elle peut être transmise en tant qu'argument de mot-clé:aboutit à
[1, 2, 3]
toutes les versions de Python prises en charge.la source
Si vous avez seulement besoin d'encoder des ensembles, pas des objets Python généraux, et que vous voulez le garder facilement lisible par l'homme, une version simplifiée de la réponse de Raymond Hettinger peut être utilisée:
la source
Si vous avez besoin d'un simple vidage rapide et que vous ne souhaitez pas implémenter un encodeur personnalisé. Vous pouvez utiliser les éléments suivants:
json_string = json.dumps(data, iterable_as_array=True)
Cela convertira tous les ensembles (et autres itérables) en tableaux. Méfiez-vous simplement que ces champs resteront des tableaux lorsque vous analyserez le json. Si vous souhaitez conserver les types, vous devez écrire un encodeur personnalisé.
la source
Un inconvénient de la solution acceptée est que sa sortie est très spécifique à Python. C'est-à-dire que sa sortie json brute ne peut pas être observée par un humain ou chargée par un autre langage (par exemple, javascript). exemple:
Vous obtiendrez:
Je peux proposer une solution qui rétrograde l'ensemble en un dict contenant une liste à la sortie, et le retourne à un ensemble lorsqu'il est chargé en python en utilisant le même encodeur, préservant ainsi l'observabilité et l'agnosticisme du langage:
Ce qui vous permet:
Notez que la sérialisation d'un dictionnaire qui a un élément avec une clé
"__set__"
cassera ce mécanisme. Ainsi__set__
est maintenant devenu unedict
clé réservée . Évidemment, n'hésitez pas à utiliser une autre clé plus obscurcie.la source