220 votes

Python les jeux ne sont pas json serializable

J'ai un python qui contient des objets avec des __hash__ et __eq__ méthodes afin de s'assurer de l'absence de doublons sont inclus dans la collection.

J'ai besoin d'encoder en json cet ensemble de résultats, mais en passant même un ensemble vide de l' json.dumps méthode soulève une exception 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 dessus de l'ensemble. Dois-je créer un dictionnaire de l'ensemble des valeurs à l'intérieur de la méthode par défaut, et ensuite de retour à l'encodage? 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 aussi)

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 l'ensemble en cours de traduction, mais leur sont internes clés qui sont un problème.

Les objets dans le jeu 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 jeu, 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.

Un objet peut contenir une valeur de date pour l' starts, tandis qu'un autre peut avoir une autre schéma qui ne comprend pas de clés contenant "non-prmitive" des objets.

C'est pourquoi la seule solution que je pouvais penser était de prolonger la JSONEncoder pour remplacer l' 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 défaut 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 le faire ici.)

J'apprécie l'aide!

167voto

jterrace Points 21939

Vous pouvez créer un codeur qui renvoie un list lorsqu'il rencontre un set. Voici un exemple:

>>> import json
>>> class SetEncoder(json.JSONEncoder):
...    def default(self, obj):
...       if isinstance(obj, set):
...          return list(obj)
...       return json.JSONEncoder.default(self, obj)
... 
>>> json.dumps(set([1,2,3,4,5]), cls=SetEncoder)
'[1, 2, 3, 4, 5]'

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.

Pour illustré les types imbriqués, envisager de sérialiser:

>>> class Something(object):
...    pass
>>> json.dumps(set([1,2,3,4,5,Something()]), cls=SetEncoder)

Cela soulève le message d'erreur suivant:

TypeError: <__main__.Something object at 0x1691c50> is not JSON serializable

Cela indique que l'encodeur prendre l' list résultat retourné et, récursivement, appelez le sérialiseur sur ses enfants. Pour ajouter un sérialiseur pour plusieurs types, vous pouvez faire ceci:

>>> class SetEncoder(json.JSONEncoder):
...    def default(self, obj):
...       if isinstance(obj, set):
...          return list(obj)
...       if isinstance(obj, Something):
...          return 'CustomSomethingRepresentation'
...       return json.JSONEncoder.default(self, obj)
... 
>>> json.dumps(set([1,2,3,4,5,Something()]), cls=SetEncoder)
'[1, 2, 3, 4, 5, "CustomSomethingRepresentation"]'

131voto

Raymond Hettinger Points 50330

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 module json 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) le vous perdre 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.:

from json import dumps, loads, JSONEncoder, JSONDecoder
import pickle

class PythonObjectEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (list, dict, str, unicode, int, float, bool, type(None))):
            return JSONEncoder.default(self, obj)
        return {'_python_object': pickle.dumps(obj)}

def as_python_object(dct):
    if '_python_object' in dct:
        return pickle.loads(str(dct['_python_object']))
    return dct

Voici un exemple de session, en montrant qu'il peut gérer des listes, des dicts, et définit:

>>> data = [1,2,3, set(['knights', 'who', 'say', 'ni']), {'key':'value'}, Decimal('3.14')]

>>> j = dumps(data, cls=PythonObjectEncoder)

>>> loads(j, object_hook=as_python_object)
[1, 2, 3, set(['knights', 'say', 'who', 'ni']), {u'key': u'value'}, Decimal('3.14')]

Alternativement, il peut être utile d'utiliser une plus fins générales de la sérialisation de la technique tels que YAML, Tordu de la Gelée, ou Python module pickle. Ces chacun en charge une gamme de types de données.

6voto

Joseph Le Brech Points 3579

Seuls les dictionnaires, Listes et objet primitif types (int, string, bool) sont disponibles en JSON.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X