Come rendere serializzabile una classe JSON


834

Come rendere serializzabile una classe Python?

Una classe semplice:

class FileItem:
    def __init__(self, fname):
        self.fname = fname

Cosa devo fare per essere in grado di ottenere l'output di:

>>> import json

>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable

Senza l'errore


31
È un peccato che tutte le risposte sembrino rispondere alla domanda "Come posso serializzare una classe?" piuttosto che la domanda di azione "Come posso rendere una classe serializzabile?" Queste risposte presuppongono che tu stia eseguendo la serializzazione da solo, piuttosto che passare l'oggetto ad un altro modulo che lo serializza.
Kyle Delaney,

Se stai usando Python3.5 +, potresti usare jsons. Converte il tuo oggetto (e tutti i suoi attributi in modo ricorsivo ) in un dict. import jsonsvedi la risposta sotto - funziona perfettamente
tswaehn

Risposte:


551

Hai un'idea del risultato atteso? Per esempio questo farà?

>>> f  = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'

In tal caso, puoi semplicemente chiamare json.dumps(f.__dict__).

Se desideri un output più personalizzato, dovrai sottoclassare JSONEncodere implementare la tua serializzazione personalizzata.

Per un esempio banale, vedi sotto.

>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
        def default(self, o):
            return o.__dict__    

>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'

Quindi passi questa classe nel json.dumps()metodo come clskwarg:

json.dumps(cls=MyEncoder)

Se vuoi anche decodificare, dovrai fornire un'abitudine object_hookalla JSONDecoderclasse. Per es

>>> def from_json(json_object):
        if 'fname' in json_object:
            return FileItem(json_object['fname'])
>>> f = JSONDecoder(object_hook = from_json).decode('{"fname": "/foo/bar"}')
>>> f
<__main__.FileItem object at 0x9337fac>
>>> 

44
L'uso __dict__non funzionerà in tutti i casi. Se gli attributi non sono stati impostati dopo che è stata creata un'istanza dell'oggetto, __dict__potrebbe non essere completamente popolato. Nell'esempio sopra, stai bene, ma se hai attributi di classe che vuoi anche codificare, quelli non saranno elencati a __dict__meno che non siano stati modificati nella __init__chiamata della classe o in qualche altro modo dopo che l'oggetto è stato istanziato.
Kris Hardy,

8
+1, ma la from_json()funzione utilizzata come hook di oggetti dovrebbe avere else: return json_objectun'istruzione, quindi può occuparsi anche di oggetti generali.
jogojapan,

8
Anche @KrisHardy __dict__non funziona se utilizzi __slots__una nuova classe di stile.
badp,

7
È possibile utilizzare un'abitudine JSONEncodercome sopra per creare un protocollo personalizzato, ad esempio controllando l'esistenza del __json_serializable__metodo e chiamandolo per ottenere una rappresentazione serializzabile JSON dell'oggetto. Questo sarebbe in linea con gli altri modelli di Python, come __getitem__, __str__, __eq__, e __len__.
jpmc26,

5
__dict__inoltre non funzionerà in modo ricorsivo, ad esempio se un attributo del tuo oggetto è un altro oggetto.
Neel,

635

Ecco una soluzione semplice per una funzione semplice:

.toJSON() Metodo

Invece di una classe serializzabile JSON, implementare un metodo serializzatore:

import json

class Object:
    def toJSON(self):
        return json.dumps(self, default=lambda o: o.__dict__, 
            sort_keys=True, indent=4)

Quindi basta chiamarlo per serializzare:

me = Object()
me.name = "Onur"
me.age = 35
me.dog = Object()
me.dog.name = "Apollo"

print(me.toJSON())

produrrà:

{
    "age": 35,
    "dog": {
        "name": "Apollo"
    },
    "name": "Onur"
}

82
Molto limitato. Se hai un dict {"foo": "bar", "baz": "bat"}, verrà serializzato facilmente su JSON. Se invece hai {"foo": "bar", "baz": MyObject ()}, allora non puoi. La situazione ideale sarebbe che gli oggetti nidificati siano serializzati su JSON in modo ricorsivo, non esplicitamente.
Mark E. Haase,

30
Funzionerà ancora. Ti stai perdendo o.__dict___. Prova il tuo esempio: class MyObject(): def __init__(self): self.prop = 1 j = json.dumps({ "foo": "bar", "baz": MyObject() }, default=lambda o: o.__dict__)
Onur Yıldırım il

14
Questa soluzione è reversibile? Cioè È facile ricostruire l'oggetto da JSON?
Jorge Leitao,

2
@ JCLeitão No. Potresti avere due classi diverse con gli stessi campi. Gli oggetti aeb di quella classe (probabilmente con le stesse proprietà) avrebbero lo stesso a.__dict__/ b.__dict__.
Martin Thoma,

7
Questo non funziona con le datetime.datetimeistanze. Emette il seguente errore:'datetime.datetime' object has no attribute '__dict__'
Bruno Finger,

171

Per classi più complesse potresti prendere in considerazione lo strumento jsonpickle :

jsonpickle è una libreria Python per la serializzazione e la deserializzazione di oggetti Python complessi da e verso JSON.

Le librerie Python standard per la codifica di Python in JSON, come json, simplejson e demjson di stdlib, possono gestire solo primitive Python che hanno un equivalente JSON diretto (es. Dicts, list, string, ints, ecc.). jsonpickle si basa su queste librerie e consente di serializzare strutture di dati più complesse su JSON. jsonpickle è altamente configurabile ed estendibile, consentendo all'utente di scegliere il backend JSON e aggiungere ulteriori backend.

(link a jsonpickle su PyPi)


32
Venendo da C #, questo è quello che mi aspettavo. Una semplice fodera e non si scherza con le lezioni.
Jerther,

2
jsonpickle è fantastico. Ha funzionato perfettamente per un oggetto enorme, complesso e disordinato con molti livelli di classe
wisbucky

c'è un esempio del modo corretto di salvarlo in un file? La documentazione mostra solo come codificare e decodificare un jsonpickleoggetto. Inoltre, questo non è stato in grado di decodificare un dict di dadi contenenti frame di dati Panda.
user5359531

3
@ user5359531 è possibile utilizzare obj = jsonpickle.decode(file.read())e file.write(jsonpickle.encode(obj)).
Kilian Batzner,

1
Una domanda specifica per django: l'uso di jsonpickle per serializzare i dati di sessione ha la stessa vulnerabilità di pickle? (come descritto qui docs.djangoproject.com/en/1.11/topics/http/sessions/… )?
Paul Bormans,

89

La maggior parte delle risposte implica la modifica della chiamata in json.dumps () , che non è sempre possibile o desiderabile (ad esempio, può accadere all'interno di un componente del framework).

Se vuoi essere in grado di chiamare json.dumps (obj) così com'è, allora una semplice soluzione sta ereditando da dict :

class FileItem(dict):
    def __init__(self, fname):
        dict.__init__(self, fname=fname)

f = FileItem('tasks.txt')
json.dumps(f)  #No need to change anything here

Funziona se la tua classe è solo una rappresentazione di base dei dati, per cose più complicate puoi sempre impostare le chiavi in ​​modo esplicito.


2
Questa può davvero essere una bella soluzione :) Credo che per il mio caso lo sia. Vantaggi: comunichi la "forma" dell'oggetto rendendolo una classe con init, è intrinsecamente serializzabile e sembra interpretabile come repr .
PascalVKooten,

1
Anche se "dot-access" è ancora mancante :(
PascalVKooten il

2
Ahh che sembra funzionare! Grazie, non sono sicuro del perché questa non sia la risposta accettata. Concordo pienamente sul fatto che cambiare dumpsnon è una buona soluzione. A proposito, nella maggior parte dei casi probabilmente vorrai avere l' dicteredità insieme alla delega, il che significa che ne avrai alcunidict attributo di tipo all'interno della tua classe, quindi passerai questo attributo come parametro come inizializzazione qualcosa del genere super().__init__(self.elements).
cglacet,

47

Mi piace la risposta di Onur ma vorrei espandermi per includere un toJSON()metodo opzionale per serializzare gli oggetti:

def dumper(obj):
    try:
        return obj.toJSON()
    except:
        return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)

Ho trovato che questo è il miglior equilibrio tra l'utilizzo della gestione esistente json.dumpse l'introduzione personalizzata. Grazie!
Daniel Buckmaster,

12
In realtà mi piace davvero tanto; ma piuttosto che try-catchprobabilmente farebbe qualcosa del genere if 'toJSON' in obj.__attrs__():... per evitare un errore silenzioso (in caso di errore in toJSON () per qualche altra ragione se non la presenza) ... un errore che potenzialmente porta alla corruzione dei dati.
thclark,

39

Un'altra opzione è quella di avvolgere il dumping JSON nella propria classe:

import json

class FileItem:
    def __init__(self, fname):
        self.fname = fname

    def __repr__(self):
        return json.dumps(self.__dict__)

O, ancora meglio, sottoclasse di classe FileItem da una JsonSerializableclasse:

import json

class JsonSerializable(object):
    def toJson(self):
        return json.dumps(self.__dict__)

    def __repr__(self):
        return self.toJson()


class FileItem(JsonSerializable):
    def __init__(self, fname):
        self.fname = fname

test:

>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'

2
Ciao, non mi piace molto questo approccio al "codificatore personalizzato", sarebbe meglio se riesci a rendere seriazabile la tua classe json. Ci provo, provo e provo e niente. C'è qualche idea su come farlo. Il fatto è che il modulo json mette alla prova la tua classe rispetto ai tipi di pitone integrati e dice anche che per le classi personalizzate fai il tuo codificatore :). Può essere falso? Quindi potrei fare qualcosa per la mia classe in modo che si comporti come un semplice elenco di moduli json? cerco subclasscheck e instancecheck ma niente.
Bojan Radojevic,

@ADRENALIN Potresti ereditare da un tipo primario (probabilmente dict), se tutti i valori degli attributi di classe sono serializzabili e non ti dispiace hack. Puoi anche usare jsonpickle o json_tricks o qualcosa del genere invece di quello standard (ancora un codificatore personalizzato, ma non uno che devi scrivere o chiamare). Il primo sottotitola l'istanza, il secondo lo memorizza come dettato di attributi, che è possibile modificare implementando __json__encode__/ __json_decode__(divulgazione: ho creato l'ultimo).
Segna il

30

Aggiungi il to_jsonmetodo alla tua classe in questo modo:

def to_json(self):
  return self.message # or how you want it to be serialized

E aggiungi questo codice (da questa risposta ) , da qualche parte nella parte superiore di tutto:

from json import JSONEncoder

def _default(self, obj):
    return getattr(obj.__class__, "to_json", _default.default)(obj)

_default.default = JSONEncoder().default
JSONEncoder.default = _default

In questo modo il modulo json verrà applicato alla patch scimmia quando viene importato, quindi JSONEncoder.default () verifica automaticamente la presenza di uno speciale metodo "to_json ()" e lo utilizza per codificare l'oggetto se trovato.

Proprio come ha detto Onur, ma questa volta non è necessario aggiornare tutti json.dumps()nel progetto.


6
Grazie tante! Questa è l'unica risposta che mi permette di fare ciò che voglio: essere in grado di serializzare un oggetto senza modificare il codice esistente. Gli altri metodi per lo più non funzionano per me. L'oggetto è definito in una libreria di terze parti e anche il codice di serializzazione è di terze parti. Cambiarli sarà imbarazzante. Con il tuo metodo, devo solo fare TheObject.to_json = my_serializer.
Yongwei Wu,

24

Mi sono imbattuto in questo problema l'altro giorno e ho implementato una versione più generale di un Encoder per oggetti Python in grado di gestire oggetti nidificati e campi ereditati :

import json
import inspect

class ObjectEncoder(json.JSONEncoder):
    def default(self, obj):
        if hasattr(obj, "to_json"):
            return self.default(obj.to_json())
        elif hasattr(obj, "__dict__"):
            d = dict(
                (key, value)
                for key, value in inspect.getmembers(obj)
                if not key.startswith("__")
                and not inspect.isabstract(value)
                and not inspect.isbuiltin(value)
                and not inspect.isfunction(value)
                and not inspect.isgenerator(value)
                and not inspect.isgeneratorfunction(value)
                and not inspect.ismethod(value)
                and not inspect.ismethoddescriptor(value)
                and not inspect.isroutine(value)
            )
            return self.default(d)
        return obj

Esempio:

class C(object):
    c = "NO"
    def to_json(self):
        return {"c": "YES"}

class B(object):
    b = "B"
    i = "I"
    def __init__(self, y):
        self.y = y

    def f(self):
        print "f"

class A(B):
    a = "A"
    def __init__(self):
        self.b = [{"ab": B("y")}]
        self.c = C()

print json.dumps(A(), cls=ObjectEncoder, indent=2, sort_keys=True)

Risultato:

{
  "a": "A", 
  "b": [
    {
      "ab": {
        "b": "B", 
        "i": "I", 
        "y": "y"
      }
    }
  ], 
  "c": {
    "c": "YES"
  }, 
  "i": "I"
}

1
Anche se questo è un po 'vecchio ... Sto affrontando un errore di importazione circolare. Quindi invece return objdell'ultima riga ho fatto questo return super(ObjectEncoder, self).default(obj). Riferimento QUI
SomeTypeFoo

24

Se stai usando Python3.5 +, puoi usarlo jsons. Converte il tuo oggetto (e tutti i suoi attributi in modo ricorsivo) in un dict.

import jsons

a_dict = jsons.dump(your_object)

O se volevi una stringa:

a_str = jsons.dumps(your_object)

O se la tua classe è stata implementata jsons.JsonSerializable:

a_dict = your_object.json

3
Se sei in grado di usare Python 3.7+, ho scoperto che la soluzione più pulita per convertire le classi python in dicts e stringhe JSON (e viceversa) è quella di mescolare la jsonslibreria con i dataclass . Finora tutto bene per me!
Ruluk,

3
Questa è una libreria esterna, non integrata nell'installazione standard di Python.
Noumenon,

solo per la classe che ha l' attributo slot
yehudahs

Puoi, ma non è necessario utilizzare gli slot . Solo quando esegui il dumping in base alla firma di una classe specifica avrai bisogno di slot . Nella prossima versione 1.1.0 anche questo non è più il caso.
UR

11
import simplejson

class User(object):
    def __init__(self, name, mail):
        self.name = name
        self.mail = mail

    def _asdict(self):
        return self.__dict__

print(simplejson.dumps(User('alice', 'alice@mail.com')))

se usi lo standard json, devi definire una defaultfunzione

import json
def default(o):
    return o._asdict()

print(json.dumps(User('alice', 'alice@mail.com'), default=default))

2
L'ho semplificato rimuovendo la funzione _asdict con un lambda json.dumps(User('alice', 'alice@mail.com'), default=lambda x: x.__dict__)
JustEngland,

8

jsonè limitato in termini di oggetti che può stampare e jsonpickle(potrebbe essere necessario un pip install jsonpickle) è limitato in termini di non poter indentare il testo. Se desideri ispezionare il contenuto di un oggetto di cui non puoi modificare la classe, non riesco ancora a trovare un modo più semplice di:

 import json
 import jsonpickle
 ...
 print  json.dumps(json.loads(jsonpickle.encode(object)), indent=2)

Nota: che ancora non possono stampare i metodi dell'oggetto.


6

Questa classe può fare il trucco, converte l'oggetto in json standard.

import json


class Serializer(object):
    @staticmethod
    def serialize(object):
        return json.dumps(object, default=lambda o: o.__dict__.values()[0])

utilizzo:

Serializer.serialize(my_object)

lavorando in python2.7e python3.


Mi è piaciuto di più questo metodo. Ho riscontrato problemi durante il tentativo di serializzare oggetti più complessi in cui membri / metodi non sono serializzabili. Ecco la mia implementazione che funziona su più oggetti: `` `serial Serializer (oggetto): @staticmethod def serialize (obj): def check (o): per k, v in o .__ dict __. Items (): try: _ = json .dumps (v) o .__ dict __ [k] = v tranne TypeError: o .__ dict __ [k] = str (v) return o return json.dumps (check (obj) .__ dict__, indent = 2) `` `
Will Charlton,

4
import json

class Foo(object):
    def __init__(self):
        self.bar = 'baz'
        self._qux = 'flub'

    def somemethod(self):
        pass

def default(instance):
    return {k: v
            for k, v in vars(instance).items()
            if not str(k).startswith('_')}

json_foo = json.dumps(Foo(), default=default)
assert '{"bar": "baz"}' == json_foo

print(json_foo)

Da doc : il parametro default(obj)è una funzione che dovrebbe restituire una versione serializzabile di obj o aumentare TypeError. L'impostazione predefinita defaultgenera semplicemente TypeError.
luckydonald,

4

jaraco ha dato una risposta abbastanza chiara . Avevo bisogno di sistemare alcune cose minori, ma questo funziona:

Codice

# Your custom class
class MyCustom(object):
    def __json__(self):
        return {
            'a': self.a,
            'b': self.b,
            '__python__': 'mymodule.submodule:MyCustom.from_json',
        }

    to_json = __json__  # supported by simplejson

    @classmethod
    def from_json(cls, json):
        obj = cls()
        obj.a = json['a']
        obj.b = json['b']
        return obj

# Dumping and loading
import simplejson

obj = MyCustom()
obj.a = 3
obj.b = 4

json = simplejson.dumps(obj, for_json=True)

# Two-step loading
obj2_dict = simplejson.loads(json)
obj2 = MyCustom.from_json(obj2_dict)

# Make sure we have the correct thing
assert isinstance(obj2, MyCustom)
assert obj2.__dict__ == obj.__dict__

Si noti che sono necessari due passaggi per il caricamento. Per ora, la __python__proprietà non è utilizzata.

Quanto è comune questo?

Utilizzando il metodo di AlJohri , controllo la popolarità degli approcci:

Serializzazione (Python -> JSON):

Deserializzazione (JSON -> Python):


4

Questo ha funzionato bene per me:

class JsonSerializable(object):

    def serialize(self):
        return json.dumps(self.__dict__)

    def __repr__(self):
        return self.serialize()

    @staticmethod
    def dumper(obj):
        if "serialize" in dir(obj):
            return obj.serialize()

        return obj.__dict__

e poi

class FileItem(JsonSerializable):
    ...

e

log.debug(json.dumps(<my object>, default=JsonSerializable.dumper, indent=2))

3

Se non ti dispiace installare un pacchetto per esso, puoi usare json-tricks :

pip install json-tricks

Dopo di che è solo bisogno di importare dump(s)dal json_tricksposto di JSON, e sarà di solito lavoro:

from json_tricks import dumps
json_str = dumps(cls_instance, indent=4)

che darà

{
        "__instance_type__": [
                "module_name.test_class",
                "MyTestCls"
        ],
        "attributes": {
                "attr": "val",
                "dct_attr": {
                        "hello": 42
                }
        }
}

E questo è tutto!


Funzionerà alla grande in generale. Ci sono alcune eccezioni, ad esempio se accadono cose speciali __new__o se sta succedendo più magia di metaclasse.

Ovviamente anche il caricamento funziona (altrimenti qual è il punto):

from json_tricks import loads
json_str = loads(json_str)

Ciò presuppone che module_name.test_class.MyTestClspossano essere importati e non siano stati modificati in modi non compatibili. Riceverai un'istanza , non un dizionario o qualcosa del genere, e dovrebbe essere una copia identica a quella che hai scaricato.

Se vuoi personalizzare il modo in cui qualcosa viene (de) serializzato, puoi aggiungere metodi speciali alla tua classe, in questo modo:

class CustomEncodeCls:
        def __init__(self):
                self.relevant = 42
                self.irrelevant = 37

        def __json_encode__(self):
                # should return primitive, serializable types like dict, list, int, string, float...
                return {'relevant': self.relevant}

        def __json_decode__(self, **attrs):
                # should initialize all properties; note that __init__ is not called implicitly
                self.relevant = attrs['relevant']
                self.irrelevant = 12

che serializza solo una parte dei parametri degli attributi, come esempio.

E come bonus gratuito, ottieni (de) serializzazione di array intorpiditi, data e orari, mappe ordinate, oltre alla possibilità di includere commenti in json.

Disclaimer: ho creato json_tricks , perché ho avuto lo stesso problema con te.


1
Ho appena testato json_tricks e ha funzionato abbellire (nel 2019).
pauljohn32,

2

jsonweb sembra essere la soluzione migliore per me. Vedi http://www.jsonweb.info/en/latest/

from jsonweb.encode import to_object, dumper

@to_object()
class DataModel(object):
  def __init__(self, id, value):
   self.id = id
   self.value = value

>>> data = DataModel(5, "foo")
>>> dumper(data)
'{"__type__": "DataModel", "id": 5, "value": "foo"}'

Funziona bene con oggetti nidificati? Compreso la decodifica e la codifica
Simone Zandara,

1

Ecco i miei 3 centesimi ...
Questo dimostra una serializzazione json esplicita per un oggetto Python simile ad un albero.
Nota: se in realtà volevi un codice come questo, puoi usare la classe twistata FilePath .

import json, sys, os

class File:
    def __init__(self, path):
        self.path = path

    def isdir(self):
        return os.path.isdir(self.path)

    def isfile(self):
        return os.path.isfile(self.path)

    def children(self):        
        return [File(os.path.join(self.path, f)) 
                for f in os.listdir(self.path)]

    def getsize(self):        
        return os.path.getsize(self.path)

    def getModificationTime(self):
        return os.path.getmtime(self.path)

def _default(o):
    d = {}
    d['path'] = o.path
    d['isFile'] = o.isfile()
    d['isDir'] = o.isdir()
    d['mtime'] = int(o.getModificationTime())
    d['size'] = o.getsize() if o.isfile() else 0
    if o.isdir(): d['children'] = o.children()
    return d

folder = os.path.abspath('.')
json.dump(File(folder), sys.stdout, default=_default)

1

Ho riscontrato questo problema quando ho provato a memorizzare il modello di Peewee in PostgreSQL JSONField.

Dopo aver lottato per un po ', ecco la soluzione generale.

La chiave della mia soluzione sta passando attraverso il codice sorgente di Python e rendendosi conto che la documentazione del codice (descritta qui ) spiega già come estendere l'esistente json.dumpsper supportare altri tipi di dati.

Supponiamo che tu abbia attualmente un modello che contiene alcuni campi che non sono serializzabili su JSON e che il modello che contiene il campo JSON originariamente assomiglia a questo:

class SomeClass(Model):
    json_field = JSONField()

Basta definire un'usanza JSONEncodercome questa:

class CustomJsonEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, SomeTypeUnsupportedByJsonDumps):
            return < whatever value you want >
        return json.JSONEncoder.default(self, obj)

    @staticmethod
    def json_dumper(obj):
        return json.dumps(obj, cls=CustomJsonEncoder)

E poi usalo nel tuo JSONFieldlike qui sotto:

class SomeClass(Model):
    json_field = JSONField(dumps=CustomJsonEncoder.json_dumper)

La chiave è il default(self, obj)metodo sopra. Per ogni singolo ... is not JSON serializablereclamo ricevuto da Python, basta aggiungere il codice per gestire il tipo non serializzabile-JSON (come Enumodatetime )

Ad esempio, ecco come supporto una classe che eredita da Enum:

class TransactionType(Enum):
   CURRENT = 1
   STACKED = 2

   def default(self, obj):
       if isinstance(obj, TransactionType):
           return obj.value
       return json.JSONEncoder.default(self, obj)

Infine, con il codice implementato come sopra, puoi semplicemente convertire qualsiasi modello Peewee in un oggetto seriazabile JSON come sotto:

peewee_model = WhateverPeeweeModel()
new_model = SomeClass()
new_model.json_field = model_to_dict(peewee_model)

Anche se il codice sopra era (in qualche modo) specifico di Peewee, ma penso:

  1. È applicabile ad altri ORM (Django, ecc.) In generale
  2. Inoltre, se hai capito come json.dumpsfunziona, questa soluzione funziona anche con Python (sans ORM) in generale

Per qualsiasi domanda, si prega di pubblicare nella sezione commenti. Grazie!


1

Questa funzione utilizza la ricorsione per scorrere su ogni parte del dizionario e quindi chiama i metodi repr () delle classi che non sono tipi incorporati.

def sterilize(obj):
    object_type = type(obj)
    if isinstance(obj, dict):
        return {k: sterilize(v) for k, v in obj.items()}
    elif object_type in (list, tuple):
        return [sterilize(v) for v in obj]
    elif object_type in (str, int, bool):
        return obj
    else:
        return obj.__repr__()


0

Ho trovato la mia soluzione. Utilizzare questo metodo, passare qualsiasi documento ( dict , list , ObjectId ecc.) Per serializzare.

def getSerializable(doc):
    # check if it's a list
    if isinstance(doc, list):
        for i, val in enumerate(doc):
            doc[i] = getSerializable(doc[i])
        return doc

    # check if it's a dict
    if isinstance(doc, dict):
        for key in doc.keys():
            doc[key] = getSerializable(doc[key])
        return doc

    # Process ObjectId
    if isinstance(doc, ObjectId):
        doc = str(doc)
        return doc

    # Use any other custom serializting stuff here...

    # For the rest of stuff
    return doc

0

Ho scelto di utilizzare i decoratori per risolvere il problema di serializzazione degli oggetti datetime. Ecco il mio codice:

#myjson.py
#Author: jmooremcc 7/16/2017

import json
from datetime import datetime, date, time, timedelta
"""
This module uses decorators to serialize date objects using json
The filename is myjson.py
In another module you simply add the following import statement:
    from myjson import json

json.dumps and json.dump will then correctly serialize datetime and date 
objects
"""

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

    if isinstance(obj, (datetime, date)):
        serial = str(obj)
        return serial
    raise TypeError ("Type %s not serializable" % type(obj))


def FixDumps(fn):
    def hook(obj):
        return fn(obj, default=json_serial)

    return hook

def FixDump(fn):
    def hook(obj, fp):
        return fn(obj,fp, default=json_serial)

    return hook


json.dumps=FixDumps(json.dumps)
json.dump=FixDump(json.dump)


if __name__=="__main__":
    today=datetime.now()
    data={'atime':today, 'greet':'Hello'}
    str=json.dumps(data)
    print str

Importando il modulo sopra, i miei altri moduli usano json in modo normale (senza specificare la parola chiave predefinita) per serializzare i dati che contengono oggetti data e ora. Il codice serializzatore datetime viene chiamato automaticamente per json.dumps e json.dump.


0

Mi è piaciuto di più il metodo di Lost Koder. Ho riscontrato problemi durante il tentativo di serializzare oggetti più complessi in cui membri / metodi non sono serializzabili. Ecco la mia implementazione che funziona su più oggetti:

class Serializer(object):
    @staticmethod
    def serialize(obj):
        def check(o):
            for k, v in o.__dict__.items():
                try:
                    _ = json.dumps(v)
                    o.__dict__[k] = v
                except TypeError:
                    o.__dict__[k] = str(v)
            return o
        return json.dumps(check(obj).__dict__, indent=2)

0

Se sei in grado di installare un pacchetto, ti consiglio di provare Dill , che ha funzionato bene per il mio progetto. Una cosa bella di questo pacchetto è che ha la stessa interfaccia di pickle, quindi se hai già usato il pickletuo progetto puoi semplicemente sostituiredill e vedere se lo script viene eseguito, senza cambiare alcun codice. Quindi è una soluzione molto economica da provare!

(Anti-divulgazione completa: non sono in alcun modo affiliato e non ho mai contribuito al progetto Dill.)

Installa il pacchetto:

pip install dill

Quindi modifica il codice da importare dillanziché pickle:

# import pickle
import dill as pickle

Esegui il tuo script e vedi se funziona. (In tal caso, potresti voler ripulire il codice in modo da non oscurare più il filepickle nome modulo!)

Alcuni dettagli sui tipi di dati che dillpossono e non possono serializzare, dalla pagina del progetto :

dill può decapare i seguenti tipi standard:

nessuna, tipo, bool, int, long, float, complex, str, unicode, tuple, list, dict, file, buffer, builtin, classi di stile vecchie e nuove, istanze di classi di stile vecchie e nuove, set, frozenset, array , funzioni, eccezioni

dill può anche decapare tipi standard più "esotici":

funzioni con rese, funzioni nidificate, lambdas, cella, metodo, metodo non associato, modulo, codice, wrapper metodo, dictproxy, metododescriptor, getsetdescriptor, membro descrittore, wrapperdescriptor, xrange, slice, non implementato, ellissi, esci

dill non è ancora possibile decapare questi tipi standard:

frame, generatore, traceback



0

Per aggiungere un'altra opzione: è possibile utilizzare il attrspacchetto e il asdictmetodo.

class ObjectEncoder(JSONEncoder):
    def default(self, o):
        return attr.asdict(o)

json.dumps(objects, cls=ObjectEncoder)

e per riconvertire

def from_json(o):
    if '_obj_name' in o:
        type_ = o['_obj_name']
        del o['_obj_name']
        return globals()[type_](**o)
    else:
        return o

data = JSONDecoder(object_hook=from_json).decode(data)

la classe si presenta così

@attr.s
class Foo(object):
    x = attr.ib()
    _obj_name = attr.ib(init=False, default='Foo')

0

Oltre alla risposta di Onur , potresti voler affrontare il tipo di data e ora come di seguito.
(per gestire: l'oggetto 'datetime.datetime' non ha eccezioni di attributo ' dict '.)

def datetime_option(value):
    if isinstance(value, datetime.date):
        return value.timestamp()
    else:
        return value.__dict__

Uso:

def toJSON(self):
    return json.dumps(self, default=datetime_option, sort_keys=True, indent=4)

0

Per prima cosa dobbiamo rendere il nostro oggetto conforme a JSON, in modo da poterlo scaricare usando il modulo JSON standard. L'ho fatto in questo modo:

def serialize(o):
    if isinstance(o, dict):
        return {k:serialize(v) for k,v in o.items()}
    if isinstance(o, list):
        return [serialize(e) for e in o]
    if isinstance(o, bytes):
        return o.decode("utf-8")
    return o

0

Basandosi su Quinten Cabo 's risposta :

def sterilize(obj):
    if type(obj) in (str, float, int, bool, type(None)):
        return obj
    elif isinstance(obj, dict):
        return {k: sterilize(v) for k, v in obj.items()}
    elif hasattr(obj, '__iter__') and callable(obj.__iter__):
        return [sterilize(v) for v in obj]
    elif hasattr(obj, '__dict__'):
        return {k: sterilize(v) for k, v in obj.__dict__.items() if k not in ['__module__', '__dict__', '__weakref__', '__doc__']}
    else:
        return repr(obj)

Le differenze sono

  1. Funziona per qualsiasi iterabile anziché solo liste tuple(funziona per array NumPy, ecc.)
  2. Funziona con tipi dinamici (quelli che contengono a __dict__).
  3. Include tipi nativi floate Nonequindi non vengono convertiti in stringa.

Lasciare come esercizio al lettore è gestire __slots__, le classi che sono sia iterabili che hanno membri, le classi che sono dizionari e hanno anche membri, ecc.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.