Comment rendre une classe Python sérialisable?
Une classe simple:
class FileItem:
def __init__(self, fname):
self.fname = fname
Que dois-je faire pour pouvoir obtenir la sortie de:
>>> import json
>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable
Sans l'erreur
python
json
serialization
Sergey
la source
la source
import jsons
voir la réponse ci-dessous - cela fonctionne parfaitement bienRéponses:
Avez-vous une idée de la sortie attendue? Par exemple, cela fera-t-il?
Dans ce cas, vous pouvez simplement appeler
json.dumps(f.__dict__)
.Si vous souhaitez une sortie plus personnalisée, vous devrez sous
JSONEncoder
- classer et implémenter votre propre sérialisation personnalisée.Pour un exemple trivial, voir ci-dessous.
Ensuite, vous passez cette classe dans la
json.dumps()
méthode en tant quecls
kwarg:Si vous souhaitez également décoder, vous devrez fournir une personnalisation
object_hook
à laJSONDecoder
classe. Par exemplela source
__dict__
ne fonctionnera pas dans tous les cas. Si les attributs n'ont pas été définis après que l'objet a été instancié, ils__dict__
peuvent ne pas être entièrement remplis. Dans l'exemple ci-dessus, tout va bien, mais si vous avez également des attributs de classe que vous souhaitez coder, ils ne seront pas répertoriés__dict__
sauf s'ils ont été modifiés dans l'__init__
appel de la classe ou d'une autre manière après l'instanciation de l'objet.from_json()
fonction utilisée comme objet-hook devrait avoir uneelse: return json_object
instruction, afin qu'elle puisse également traiter des objets généraux.__dict__
ne fonctionne pas non plus si vous utilisez__slots__
une nouvelle classe de style.JSONEncoder
comme ci-dessus pour créer un protocole personnalisé, comme vérifier l'existence de la__json_serializable__
méthode et l'appeler pour obtenir une représentation sérialisable JSON de l'objet. Ce serait conforme aux autres modèles de Python, comme__getitem__
,__str__
,__eq__
, et__len__
.__dict__
ne fonctionnera pas non plus de manière récursive, par exemple, si un attribut de votre objet est un autre objet.Voici une solution simple pour une fonctionnalité simple:
.toJSON()
MéthodeAu lieu d'une classe sérialisable JSON, implémentez une méthode de sérialisation:
Il suffit donc de l'appeler pour sérialiser:
affichera:
la source
o.__dict___
. Essayez votre propre exemple:class MyObject(): def __init__(self): self.prop = 1 j = json.dumps({ "foo": "bar", "baz": MyObject() }, default=lambda o: o.__dict__)
a.__dict__
/b.__dict__
.datetime.datetime
instances. Il renvoie l'erreur suivante:'datetime.datetime' object has no attribute '__dict__'
Pour les classes plus complexes, vous pouvez envisager l'outil jsonpickle :
(lien vers jsonpickle sur PyPi)
la source
jsonpickle
objet. En outre, cela n'a pas pu décoder un dict de dict contenant des trames de données pandas.obj = jsonpickle.decode(file.read())
etfile.write(jsonpickle.encode(obj))
.La plupart des réponses impliquent de changer l'appel à json.dumps () , ce qui n'est pas toujours possible ou souhaitable (cela peut arriver à l'intérieur d'un composant de framework par exemple).
Si vous voulez pouvoir appeler json.dumps (obj) tel quel , alors une solution simple hérite de dict :
Cela fonctionne si votre classe n'est qu'une représentation de données de base, pour des choses plus délicates, vous pouvez toujours définir des clés de manière explicite.
la source
dumps
n'est pas une bonne solution. Soit dit en passant, dans la plupart des cas, vous souhaiterez probablement avoir l'dict
héritage avec la délégation, ce qui signifie que vous aurez undict
attribut de type à l'intérieur de votre classe, vous passerez ensuite cet attribut comme paramètre comme initialisation quelque chose commesuper().__init__(self.elements)
.J'aime la réponse d'Onur, mais je développerais pour inclure une
toJSON()
méthode facultative permettant aux objets de se sérialiser:la source
json.dumps
gestion existante et l'introduction d'une gestion personnalisée. Merci!try-catch
faire probablement quelque chose commeif 'toJSON' in obj.__attrs__():
... pour éviter un échec silencieux (en cas d'échec dans toJSON () pour une autre raison qu'il n'est pas là) ... un échec qui mène potentiellement à une corruption des données.Une autre option consiste à envelopper le dumping JSON dans sa propre classe:
Ou, mieux encore, sous-classer la classe FileItem d'une
JsonSerializable
classe:Essai:
la source
__json__encode__
/__json_decode__
(divulgation: j'ai fait la dernière).Ajoutez simplement une
to_json
méthode à votre classe comme ceci:Et ajoutez ce code (à partir de cette réponse ) , quelque part en haut de tout:
Ce module json corrige les singes lors de son importation. JSONEncoder.default () recherche automatiquement une méthode spéciale "to_json ()" et l'utilise pour coder l'objet s'il est trouvé.
Tout comme Onur l'a dit, mais cette fois, vous n'avez pas à mettre à jour chaque élément
json.dumps()
de votre projet.la source
TheObject.to_json = my_serializer
.Je suis tombé sur ce problème l'autre jour et j'ai implémenté une version plus générale d'un encodeur pour les objets Python qui peut gérer les objets imbriqués et les champs hérités :
Exemple:
Résultat:
la source
return obj
la dernière ligne, je l'ai faitreturn super(ObjectEncoder, self).default(obj)
. Référence ICISi vous utilisez Python3.5 +, vous pouvez utiliser
jsons
. Il convertira votre objet (et tous ses attributs récursivement) en dict.Ou si vous vouliez une chaîne:
Ou si votre classe a implémenté
jsons.JsonSerializable
:la source
jsons
bibliothèque avec des classes de données . Jusqu'à présent, tant mieux pour moi!si utilisation standard
json
, vous devez définir unedefault
fonctionla source
json.dumps(User('alice', '[email protected]'), default=lambda x: x.__dict__)
json
est limité en termes d'objets qu'il peut imprimer etjsonpickle
(vous en aurez peut-être besoinpip install jsonpickle
) est limité en termes qu'il ne peut pas indenter le texte. Si vous souhaitez inspecter le contenu d'un objet dont vous ne pouvez pas changer la classe, je ne pouvais toujours pas trouver de voie plus droite que:Remarque: ils ne peuvent toujours pas imprimer les méthodes d'objet.
la source
Cette classe peut faire l'affaire, elle convertit l'objet en json standard.
usage:
travaillant dans
python2.7
etpython3
.la source
la source
default(obj)
est une fonction qui devrait renvoyer une version sérialisable d'obj ou augmenter TypeError. La valeur par défautdefault
déclenche simplement TypeError.jaraco a donné une réponse assez soignée. J'avais besoin de réparer quelques petites choses, mais cela fonctionne:
Code
Notez que nous avons besoin de deux étapes pour le chargement. Pour l'instant, la
__python__
propriété n'est pas utilisée.Est-ce courant?
En utilisant la méthode d' AlJohri , je vérifie la popularité des approches:
Sérialisation (Python -> JSON):
to_json
: 266595 le 2018-06-27toJSON
: 96307 le 2018-06-27__json__
: 8,504 le 2018-06-27for_json
: 6937 le 2018-06-27Désérialisation (JSON -> Python):
from_json
: 226101 le 2018-06-27la source
Cela a bien fonctionné pour moi:
et alors
et
la source
Si cela ne vous dérange pas d'installer un paquet pour lui, vous pouvez utiliser json-tricks :
Après cela, il vous suffit d'importer
dump(s)
depuisjson_tricks
au lieu de json, et cela fonctionnera généralement:ce qui donnera
Et c'est tout!
Cela fonctionnera très bien en général. Il y a quelques exceptions, par exemple si des choses spéciales se produisent
__new__
ou s'il y a plus de magie de métaclasse.Évidemment, le chargement fonctionne également (sinon à quoi ça sert):
Cela suppose qu'il
module_name.test_class.MyTestCls
peut être importé et n'a pas changé de manière non compatible. Vous récupérerez une instance , pas un dictionnaire ou quelque chose, et ce devrait être une copie identique à celle que vous avez sauvegardée.Si vous souhaitez personnaliser la façon dont quelque chose est (dé) sérialisé, vous pouvez ajouter des méthodes spéciales à votre classe, comme ceci:
qui sérialise seulement une partie des paramètres d'attributs, par exemple.
Et en bonus gratuit, vous obtenez la (dé) sérialisation des tableaux numpy, la date et les heures, les cartes commandées, ainsi que la possibilité d'inclure des commentaires dans json.
Avertissement: J'ai créé json_tricks , car j'ai eu le même problème que vous.
la source
jsonweb semble être la meilleure solution pour moi. Voir http://www.jsonweb.info/en/latest/
la source
Voici mes 3 cents ...
Cela démontre la sérialisation json explicite pour un objet python arborescent.
Remarque: Si vous vouliez réellement du code comme celui-ci, vous pouvez utiliser la classe FilePath torsadée .
la source
J'ai rencontré ce problème lorsque j'ai essayé de stocker le modèle de Peewee dans PostgreSQL
JSONField
.Après avoir lutté pendant un certain temps, voici la solution générale.
La clé de ma solution passe par le code source de Python et se rend compte que la documentation du code (décrite ici ) explique déjà comment étendre l'existant
json.dumps
pour prendre en charge d'autres types de données.Supposons que vous ayez actuellement un modèle qui contient certains champs qui ne sont pas sérialisables en JSON et que le modèle qui contient le champ JSON ressemble à l'origine à ceci:
Définissez simplement une coutume
JSONEncoder
comme celle-ci:Et puis il suffit de l'utiliser dans votre
JSONField
comme ci-dessous:La clé est la
default(self, obj)
méthode ci-dessus. Pour chaque... is not JSON serializable
plainte que vous recevez de Python, ajoutez simplement du code pour gérer le type unserializable-to-JSON (tel queEnum
oudatetime
)Par exemple, voici comment je prends en charge une classe héritant de
Enum
:Enfin, avec le code implémenté comme ci-dessus, vous pouvez simplement convertir n'importe quel modèle Peewee en un objet JSON-seriazable comme ci-dessous:
Bien que le code ci-dessus soit (quelque peu) spécifique à Peewee, mais je pense:
json.dumps
fonctionne, cette solution fonctionne également avec Python (sans ORM) en général aussiPour toute question, veuillez poster dans la section commentaires. Merci!
la source
Cette fonction utilise la récursivité pour itérer sur chaque partie du dictionnaire, puis appelle les méthodes repr () des classes qui ne sont pas des types intégrés.
la source
Il s'agit d'une petite bibliothèque qui sérialise un objet avec tous ses enfants en JSON et le réanalyse également:
https://github.com/Toubs/PyJSONSerialization/
la source
J'ai trouvé ma propre solution. Utilisez cette méthode, passez n'importe quel document ( dict , list , ObjectId etc.) à sérialiser.
la source
J'ai choisi d'utiliser des décorateurs pour résoudre le problème de sérialisation des objets datetime. Voici mon code:
En important le module ci-dessus, mes autres modules utilisent json de manière normale (sans spécifier le mot-clé par défaut) pour sérialiser des données contenant des objets date-heure. Le code de sérialisation datetime est automatiquement appelé pour json.dumps et json.dump.
la source
J'ai préféré la méthode de Lost Koder. J'ai rencontré des problèmes lors de la tentative de sérialisation d'objets plus complexes dont les membres / méthodes ne sont pas sérialisables. Voici mon implémentation qui fonctionne sur plus d'objets:
la source
Si vous êtes en mesure d'installer un package, je vous recommande d'essayer l' aneth , qui a très bien fonctionné pour mon projet. Une bonne chose à propos de ce package est qu'il a la même interface que
pickle
, donc si vous avez déjà utilisépickle
dans votre projet, vous pouvez simplement le remplacerdill
et voir si le script s'exécute, sans changer de code. C'est donc une solution très bon marché à essayer!(Anti-divulgation complète: je ne suis en aucun cas affilié et n'ai jamais contribué au projet Dill.)
Installez le package:
Modifiez ensuite votre code à importer
dill
au lieu depickle
:Exécutez votre script et voyez si cela fonctionne. (Si c'est le cas, vous souhaiterez peut-être nettoyer votre code afin de ne plus masquer le
pickle
nom du module!)Quelques détails sur les types de données qui
dill
peuvent et ne peuvent pas être sérialisés, à partir de la page du projet :la source
Je ne vois aucune mention ici de version en série ou de backcompat, donc je posterai ma solution que j'utilise un peu. J'ai probablement beaucoup plus à apprendre, en particulier Java et Javascript sont probablement plus matures que moi ici, mais voilà
https://gist.github.com/andy-d/b7878d0044a4242c0498ed6d67fd50fe
la source
Pour ajouter une autre option: Vous pouvez utiliser le
attrs
package et laasdict
méthode.et pour reconvertir
la classe ressemble à ceci
la source
En plus de la réponse d'Onur , vous voudrez peut-être traiter le type datetime comme ci-dessous.
(afin de gérer: l'objet 'datetime.datetime' n'a pas d'attribut ' dict ' d'exception.)
Usage:
la source
Nous devons d'abord rendre notre objet conforme à JSON, afin que nous puissions le vider à l'aide du module JSON standard. Je l'ai fait de cette façon:
la source
S'appuyant sur la réponse de Quinten Cabo :
Les différences sont
list
ettuple
(cela fonctionne pour les tableaux NumPy, etc.)__dict__
).float
etNone
donc ils ne sont pas convertis en chaîne.Laissé comme un exercice au lecteur est de gérer
__slots__
, les classes qui sont à la fois itérables et ont des membres, les classes qui sont des dictionnaires et ont également des membres, etc.la source