259 votes

Sérialisation de l'instance de classe en JSON

J'essaie de créer une représentation en chaîne JSON d'une instance de classe et je rencontre des difficultés. Disons que la classe est construite comme ceci :

class testclass:
    value1 = "a"
    value2 = "b"

Un appel au json.dumps est fait comme ceci :

t = testclass()
json.dumps(t)

Il échoue et me dit que la classe de test n'est pas sérialisable par JSON.

TypeError: <__main__.testclass object at 0x000000000227A400> is not JSON serializable

J'ai également essayé d'utiliser le module pickle :

t = testclass()
print(pickle.dumps(t, pickle.HIGHEST_PROTOCOL))

Et il donne des informations sur l'instance de classe mais pas un contenu sérialisé de l'instance de classe.

b'\x80\x03c__main__\ntestclass\nq\x00)\x81q\x01}q\x02b.'

Qu'est-ce que je fais de mal ?

306voto

steveha Points 24808

Le problème de base est que l'encodeur JSON json.dumps() ne sait sérialiser qu'un ensemble limité de types d'objets par défaut, tous les types intégrés. Liste ici : https://docs.python.org/3.3/library/json.html#encoders-and-decoders

Une bonne solution serait de faire en sorte que votre classe hérite de JSONEncoder et ensuite mettre en œuvre la JSONEncoder.default() et faites en sorte que cette fonction émette le JSON correct pour votre classe.

Une solution simple serait d'appeler json.dumps() en el .__dict__ de cette instance. C'est une méthode Python standard dict et si votre classe est simple, elle sera sérialisable JSON.

class Foo(object):
    def __init__(self):
        self.x = 1
        self.y = 2

foo = Foo()
s = json.dumps(foo) # raises TypeError with "is not JSON serializable"

s = json.dumps(foo.__dict__) # s set to: {"x":1, "y":2}

L'approche ci-dessus est discutée dans cet article de blog :

     Sérialisation d'objets Python arbitraires en JSON à l'aide de _. dict _

Et, bien sûr, Python propose une fonction intégrée qui accède à .__dict__ pour vous, appelé vars() .

Donc, l'exemple ci-dessus peut également être fait comme :

s = json.dumps(vars(foo)) # s set to: {"x":1, "y":2}

88voto

Broccoli Points 379

Il y a une méthode qui fonctionne très bien pour moi et que vous pouvez essayer :

json.dumps() peut prendre un paramètre facultatif par défaut où vous pouvez spécifier une fonction de sérialisation personnalisée pour les types inconnus, qui dans mon cas ressemble à

def serialize(obj):
    """JSON serializer for objects not serializable by default json code"""

    if isinstance(obj, date):
        serial = obj.isoformat()
        return serial

    if isinstance(obj, time):
        serial = obj.isoformat()
        return serial

    return obj.__dict__

Les deux premiers ifs sont pour la sérialisation de la date et de l'heure. et ensuite il y a un obj.__dict__ retourné pour tout autre objet.

l'appel final ressemble à ça :

json.dumps(myObj, default=serialize)

C'est particulièrement bien lorsque vous sérialisez une collection et que vous ne voulez pas appeler __dict__ explicitement pour chaque objet. Ici, c'est fait pour vous automatiquement.

Jusqu'à présent, tout s'est bien passé pour moi, j'ai hâte de connaître votre avis.

79voto

codeman48 Points 623

Vous pouvez spécifier le default paramètre nommé dans le json.dumps() fonction :

json.dumps(obj, default=lambda x: x.__dict__)

Explication :

Formez les docs ( 2.7 , 3.6 ) :

``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.

(Fonctionne sur Python 2.7 et Python 3.x)

Note : Dans ce cas, vous devez instance et non pas class variables, comme l'exemple de la question tente de le faire. (Je suppose que l'auteur de la question voulait dire class instance pour être un objet d'une classe)

J'ai appris cela d'abord dans la réponse de @phihag. ici . C'est la façon la plus simple et la plus propre de faire le travail.

31voto

gies0r Points 107

Utilisation de jsonpickle

import jsonpickle

object = YourClass()
json_object = jsonpickle.encode(object)

25voto

SpiRail Points 362

Je le fais, c'est tout :

data=json.dumps(myobject.__dict__)

Ce n'est pas la réponse complète, et si vous avez une sorte de classe d'objets compliquée, vous n'obtiendrez certainement pas tout. Cependant, je l'utilise pour certains de mes objets simples.

L'une d'entre elles qui fonctionne très bien est la classe "options" que vous obtenez du module OptionParser. La voici avec la requête JSON elle-même.

  def executeJson(self, url, options):
        data=json.dumps(options.__dict__)
        if options.verbose:
            print data
        headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
        return requests.post(url, data, headers=headers)

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