Comment JSON sérialiser des jeux?
J'ai un Python set
qui contient des objets avec __hash__
et __eq__
méthodes afin de s'assurer de l'absence de doublons sont inclus dans la collection.
J'ai besoin de json-encode ce résultat set
, mais en passant même vide set
à la json.dumps
méthode soulè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 le json.JSONEncoder
classe qui a une coutume default
méthode, mais je ne suis même pas sûr où commencer dans la conversion de plus de la set
. Dois-je créer un dictionnaire de la set
valeurs au sein de la méthode par défaut, et ensuite de retour à l'encodage sur qui? Idéalement, j'aimerais faire la méthode par défaut en mesure de traiter tous les types de données que l'origine de l'encodeur étouffe (je suis à l'aide de Mongo comme une source de données afin dates semblent soulever cette erreur trop)
Tout soupçon dans la bonne direction serait appréciée.
EDIT:
Merci pour la réponse! Peut-être que j'aurais dû être plus précis.
J'ai utilisé (et upvoted) les réponses ici, afin de contourner les limitations de la set
en cours de traduction, mais il y a des clés internes qui sont un problème.
Les objets dans le set
sont des objets complexes qui traduisent __dict__
, mais ils peuvent également contenir des valeurs de leurs propriétés qui pourraient être éligibles pour les types de base dans le json de l'encodeur.
Il y a beaucoup de différents types de venir sur ce set
, et le hachage fondamentalement calcule un identifiant unique pour l'entité, mais dans le véritable esprit de NoSQL, on ne sait pas exactement ce que l'enfant objet contient.
Un objet peut contenir une valeur de date pour starts
, tandis qu'un autre peut avoir une autre schéma qui ne comprend pas de clés contenant "non-primitifs" des objets.
C'est pourquoi la seule solution que je pouvais penser était de prolonger la JSONEncoder
pour remplacer le default
méthode à son tour sur les différents cas - mais je ne suis pas sûr de savoir comment aller à ce sujet, et la documentation est ambigu. Dans les objets imbriqués, la valeur renvoyée par default
aller par touche, ou est-il un générique inclure/exclure que regarde l'ensemble de l'objet? Comment est-ce que la méthode d'accueillir imbriquée valeurs? J'ai regardé à travers les questions précédentes et n'arrive pas à trouver la meilleure approche pour le cas spécifique de l'encodage (qui, malheureusement, semble être ce que je vais avoir besoin de faire ici).
- pourquoi
dict
s? Je pense que vous voulez simplement faire unelist
de le définir et de le passer à l'encodeur... e.g:encode(list(myset))
- Au lieu d'utiliser JSON, vous pouvez utiliser YAML (JSON est essentiellement un sous-ensemble de YAML).
- Il apporte aucun avantage si? Je ne pense pas que les jeux sont parmi les universellement pris en charge types de données de YAML, et c'est moins largement pris en charge, en particulier concernant l'Api.
- Je vous remercie pour votre entrée, mais l'application frontend nécessite JSON comme un type de retour, et cette exigence est, à toutes fins fixe.
- Je suggère YAML, car il a une prise en charge native pour les deux jeux et dates.
- FWIW, ma réponse montre comment gérer l'étude cas sans casser votre capacité à utiliser les listes et les dicts. Cette approche est facilement étendu pour gérer beaucoup de différents types de données.
- Je suis dans le processus de mise en œuvre de votre solution à l'heure actuelle. Étrange coïncidence - le système de notation pour ces ensembles de données a été construit avec votre réseau de neurones code comme un guide! Peut-être vous souvenez-vous de moi vous tweet =)
Vous devez vous connecter pour publier un commentaire.
JSON notation a seulement une poignée de native types de données (objets, tableaux, chaînes de caractères, des nombres, booléens, et null), donc tout ce que sérialisé en JSON doit être exprimé en tant que l'un de ces types.
Comme indiqué dans le json module docs, cette conversion peut être effectuée automatiquement par un JSONEncoder et JSONDecoder, mais alors vous seriez en donnant une autre structure que vous pourriez avoir besoin (si vous convertissez des ensembles d'une liste, puis de vous perdre la capacité de récupérer régulièrement des listes; si vous convertissez des ensembles d'un dictionnaire à l'aide de
dict.fromkeys(s)
alors vous perdez la capacité de récupérer des dictionnaires).Une solution plus élaborée est de construire un type personnalisé qui peut coexister avec d'autres natif JSON types. Cela vous permet de stocker des structures imbriquées qui incluent des listes, ensembles, dicts, les décimales, datetime objets, etc.:
Voici un exemple de session, en montrant qu'il peut gérer des listes, des dicts, et définit:
Alternativement, il peut être utile d'utiliser un objectif général de sérialisation technique comme YAML, Twisted Jelly, ou Python module pickle. Ces chacun en charge une gamme de types de données.
if isinstance(obj, (list, dict, ... etc)):
déclaration dansPythonObjectEncoder.default()
est redondant, car il va que si l'objet est quelque chose de la base JSONEncoder classe ne peut pas gérer.JSONDecoder
mais ne l'utilise pasJSONDecoder
parce que c'est la classe de base dePythonObjectEncoder
.JSONEncoder
de documentation il semble que lesdefault()
méthode n'est appelée que "pour les objets qui ne peuvent autrement être sérialisé", ce qui signifie qu'il n'est pas nécessaire de faire laisinstance()
vérifier dans votre réponse. Cela semble workt par mes propres tests, mais je voudrais mettre lereturn {'_python_object': pickle.dumps(obj)}
à l'intérieur d'untry/except TypeError:
en caspickle.dumps()
ne peut pas traiter avec l'objet. Suis-je comprendre quelque chose?JSONEncoder
, ne pourrait-on pas simplement utiliser ledefault=
argument mot-clé de la classe standard pour spécifier une fonction qui fait quelque chose de similaire à ce quePythonObjectEncoder.default()
méthode dans votre réponse? Cela semblait fonctionner quand je l'ai testé, ainsi.Vous pouvez créer un codeur qui renvoie un
list
lorsqu'il rencontre unset
. Voici un exemple:Vous pouvez détecter d'autres types de cette façon. Si vous devez retenir que la liste est en fait un ensemble, vous pouvez utiliser un codage personnalisé. Quelque chose comme
return {'type':'set', 'list':list(obj)}
pourrait fonctionner.À illustré les types imbriqués, envisager de sérialiser:
Cela soulève l'erreur suivante:
Cela indique que l'encodeur prendre la
list
résultat retourné et, récursivement, appelez le sérialiseur sur ses enfants. Pour ajouter un sérialiseur pour plusieurs types, vous pouvez le faire:default
fonction sera appelée de nouveau, cette fois avecobj
être un objet date, vous n'avez qu'à tester pour cela et le retourner une date de représentation.SetEncoder
?Seulement dictionnaires, Listes et objet primitif types (int, string, bool) sont disponibles en JSON.
J'ai adapté Raymond Hettinger de la solution à python 3.
Voici ce qui a changé:
unicode
disparudefault
avecsuper()
base64
pour sérialiser l'bytes
type dansstr
(parce qu'il semble quebytes
en python 3 ne peut pas être convertie en JSON)json.dumps()
retourne vers/à partir de'latin1'
, en ignorant labase64
stuff qui n'est pas nécessaire.Si vous avez seulement besoin de coder des jeux, pas en général des objets Python, et que vous voulez la garder facilement lisible par l'homme, une version simplifiée de Raymond Hettinger de réponse peuvent être utilisés:
Si vous avez juste besoin de vidage rapide et ne veulent pas mettre en œuvre personnalisée de l'encodeur. Vous pouvez utiliser les éléments suivants:
json_string = json.dumps(data, iterable_as_array=True)
Cela permettra de convertir tous les jeux (et d'autres iterables) dans des tableaux. Méfiez-vous cependant que ces champs de séjour des tableaux lorsque vous analysez le json en arrière. Si vous souhaitez conserver les types, vous devez écrire personnalisé de l'encodeur.