Come superare "datetime.datetime non serializzabile JSON"?


743

Ho un dettato di base come segue:

sample = {}
sample['title'] = "String"
sample['somedate'] = somedatetimehere

Quando provo a fare jsonify(sample)ottengo:

TypeError: datetime.datetime(2012, 8, 8, 21, 46, 24, 862000) is not JSON serializable

Cosa posso fare in modo che il mio esempio di dizionario possa superare l'errore sopra riportato?

Nota: sebbene possa non essere pertinente, i dizionari sono generati dal recupero di record da mongodbdove, quando stampo str(sample['somedate']), l'output è 2012-08-08 21:46:24.862000.


1
Questo è specificamente python in generale, o forse django?
jdi,

1
Tecnicamente è specificamente Python, non sto usando Django, ma sto recuperando dischi da mongodb.
Rolando,

possibile duplicato del datetime JSON tra Python e JavaScript
jdi

Sto usando mongoengine, ma se pymongo ha modi migliori per aggirare questo problema o superarlo, per favore dillo.
Rolando,

3
La domanda collegata in sostanza ti dice di non provare a serializzare l'oggetto datetime, ma piuttosto di convertirlo in una stringa nel formato ISO comune prima di serializzare.
Thomas Kelley,

Risposte:


377

Aggiornato per il 2018

La risposta originale ospitava il modo in cui i campi "data" di MongoDB venivano rappresentati come:

{"$date": 1506816000000}

Se desideri una soluzione Python generica per la serializzazione datetimesu json, controlla la risposta di @jjmontes per una soluzione rapida che non richiede dipendenze.


Mentre stai usando mongoengine (per commenti) e pymongo è una dipendenza, pymongo ha utilità integrate per aiutare con la serializzazione json:
http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

Esempio di utilizzo (serializzazione):

from bson import json_util
import json

json.dumps(anObject, default=json_util.default)

Esempio di utilizzo (deserializzazione):

json.loads(aJsonString, object_hook=json_util.object_hook)

Django

Django fornisce un DjangoJSONEncoderserializzatore nativo che si occupa di questo tipo di correttamente.

Vedi https://docs.djangoproject.com/en/dev/topics/serialization/#djangojsonencoder

from django.core.serializers.json import DjangoJSONEncoder

return json.dumps(
  item,
  sort_keys=True,
  indent=1,
  cls=DjangoJSONEncoder
)

Una differenza che ho notato tra DjangoJSONEncodere l'utilizzo di un'usanza defaultcome questa:

import datetime
import json

def default(o):
    if isinstance(o, (datetime.date, datetime.datetime)):
        return o.isoformat()

return json.dumps(
  item,
  sort_keys=True,
  indent=1,
  default=default
)

Django toglie un po 'di dati?

 "last_login": "2018-08-03T10:51:42.990", # DjangoJSONEncoder 
 "last_login": "2018-08-03T10:51:42.990239", # default

Pertanto, in alcuni casi potrebbe essere necessario prestare attenzione a questo.


3
È buona / cattiva pratica mescolare più librerie, ad esempio avere mongoengine per l'inserimento di documenti e pymongo per query / recupero?
Rolando,

Non è una cattiva pratica, implica solo una certa dipendenza dalle librerie utilizzate dalla libreria principale. Se non riesci a ottenere ciò di cui hai bisogno da mongoengine, allora scendi a pymongo. È lo stesso con Django MongoDB. In seguito, proveresti a rimanere all'interno dell'ORM di django per mantenere lo stato agnostico del back-end. Ma a volte non puoi fare ciò di cui hai bisogno nell'astrazione, quindi abbassi un livello. In questo caso, è completamente estraneo al tuo problema poiché stai solo usando metodi di utilità per accompagnare il formato JSON.
jdi,

Sto provando questo con Flask e sembra che usando json.dump, non sono in grado di mettere un wrapper jsonify () in modo tale che ritorni in application / json. Tentativo di restituire jsonify (json.dumps (esempio, impostazione predefinita = json_util.default))
Rolando

2
@amit Non si tratta tanto di memorizzare la sintassi, quanto di imparare a leggere la documentazione e memorizzare abbastanza informazioni nella mia testa per riconoscere dove e quando devo recuperarla di nuovo. In questo caso, si potrebbe dire "Oh un oggetto personalizzato con JSON" e quindi aggiornare rapidamente su
quell'uso

2
@guyskk Non ho seguito i cambiamenti in bjson o mongo da quando l'ho scritto 5 anni fa. Ma se vuoi il controllo sulla serializzazione del datetime, allora devi scrivere la tua funzione di gestore predefinita come illustrato nella risposta data da jgbarah
jdi

619

La mia discarica JSON veloce e sporca che mangia date e tutto:

json.dumps(my_dictionary, indent=4, sort_keys=True, default=str)

14
È fantastico, ma sfortunatamente non ho capito cosa è successo? Qualcuno può spiegare questa risposta?
Kishor Pawar,

63
@KishorPawar: defaultè una funzione applicata agli oggetti che non sono serializzabili. In questo caso lo è str, quindi converte tutto ciò che non conosce in stringhe. Il che è ottimo per la serializzazione, ma non così grande durante la deserializzazione (quindi il "quick & dirty") in quanto qualsiasi cosa potrebbe essere stata ricercata senza preavviso, ad esempio una funzione o un array numpy.
Segna il

1
@Mark fantastico. Grazie. Utile quando conosci il tipo di quei valori non serializzabili come le date.
Kishor Pawar,

2
Perché sono andato per tutta la vita senza saperlo. :)
Arel,

1
@jjmontes, non funziona per tutto, ad esempio json.dumps({():1,type(None):2},default=str)rilanci TypeError, non può avere tipo o tupla.
alancalvitti,

443

Basandosi su altre risposte, una soluzione semplice basata su un serializzatore specifico che converte datetime.datetimee datetime.dateoggetti solo nelle stringhe.

from datetime import date, datetime

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

    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    raise TypeError ("Type %s not serializable" % type(obj))

Come visto, il codice verifica solo se l'oggetto è di classe datetime.datetimeo datetime.date, quindi utilizza .isoformat()per produrne una versione serializzata, secondo il formato ISO 8601, AAAA-MM-GGTHH: MM: SS (che è facilmente decodificato da JavaScript ). Se si cercano rappresentazioni serializzate più complesse, è possibile utilizzare altro codice al posto di str () (vedere altre risposte a questa domanda per esempi). Il codice termina generando un'eccezione, per gestire il caso viene chiamato con un tipo non serializzabile.

Questa funzione json_serial può essere utilizzata come segue:

from datetime import datetime
from json import dumps

print dumps(datetime.now(), default=json_serial)

I dettagli su come funziona il parametro predefinito su json.dumps sono disponibili nella sezione Utilizzo di base della documentazione del modulo json .


5
sì, la risposta corretta, più importato datetime import e se isinstance (obj, datetime.datetime), ho perso molto tempo perché non usato da datetime import datetime, comunque grazie
Sérgio

12
ma questo non spiega come deserializzarlo con il tipo corretto, no?
BlueTrin,

2
No, @BlueTrin, non è stato detto nulla al riguardo. Nel mio caso, sto deserializzando in JavaScript, che funziona immediatamente.
jgbarah,

1
Ciò causerà comportamenti imprevisti se il modulo json si aggiorna in modo da includere la serializzazione di oggetti datetime.
Giustino,

1
@serg Ma la conversione dei tempi in UTC si unificherebbe 01:00:00+01:00e 02:00:00+00:00non dovrebbero essere gli stessi, a seconda del contesto. Si riferiscono allo stesso punto nel tempo ovviamente, ma l'offset potrebbe essere un aspetto rilevante del valore.
Alfe,

211

Ho appena riscontrato questo problema e la mia soluzione è la sottoclasse json.JSONEncoder:

from datetime import datetime
import json

class DateTimeEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, datetime):
            return o.isoformat()

        return json.JSONEncoder.default(self, o)

Nella tua chiamata fai qualcosa del tipo: json.dumps(yourobj, cls=DateTimeEncoder)L' .isoformat()ho ricevuto da una delle risposte sopra.


22
aumentato perché l'implementazione di un JSONEncoder personalizzato dovrebbe essere la strada giusta da percorrere
3k

25
Non solo questa dovrebbe essere la risposta migliore, ma dovrebbe far parte del normale codificatore json. Se solo la decodifica fosse meno ambigua ...
Joost

4
Per quelli che usano Django, vedi DjangoJSONEncoder. docs.djangoproject.com/en/dev/topics/serialization/…
S. Kirby,

4
Super utile. L'ultima riga potrebbe esserereturn super(DateTimeEncoder, self).default(o)
Bob Stein il

16
Con Python 3 l'ultima riga è ancora più semplice:return super().default(o)
ariddell,

124

Converti la data in una stringa

sample['somedate'] = str( datetime.utcnow() )

10
E come potrei deserializzarlo in Python?
Wobmene,

62
Il problema è se hai molti oggetti datetime incorporati profondamente in una struttura di dati o se sono casuali. Questo non è un metodo affidabile.
Rebs,

3
per deserializzare: oDate = datetime.datetime.strptime(sDate, '%Y-%m-%d %H:%M:%S.%f'). Formati ottenuti da: docs.python.org/2/library/datetime.html
Roman

13
Sottovalutato in quanto ignora le informazioni sul fuso orario. Tieni presente che .now()utilizza l'ora locale, senza indicarlo. Almeno .utcnow()dovrebbe essere usato (e poi aggiunto un +0000 o Z)
Daniel F

1
@DanielF At least .utcnow() should be usedNon esattamente, datetime.now(timezone.utc)si consiglia, vedere l'avvertenza in: docs.python.org/3.8/library/… .
Toreno96

79

Per gli altri utenti che non necessitano o desiderano utilizzare la libreria pymongo per questo .. puoi ottenere facilmente la conversione JSON datetime con questo piccolo frammento:

def default(obj):
    """Default JSON serializer."""
    import calendar, datetime

    if isinstance(obj, datetime.datetime):
        if obj.utcoffset() is not None:
            obj = obj - obj.utcoffset()
        millis = int(
            calendar.timegm(obj.timetuple()) * 1000 +
            obj.microsecond / 1000
        )
        return millis
    raise TypeError('Not sure how to serialize %s' % (obj,))

Quindi usalo così:

import datetime, json
print json.dumps(datetime.datetime.now(), default=default)

produzione: 

'1365091796124'

1
Non dovrebbe millis=essere rientrato all'interno dell'istruzione if? Probabilmente è anche meglio usare str (obj) per ottenere il formato ISO che riterrei più comune.
Rebs,

Perché vorresti che fosse rientrato? Questo frammento funziona e l'output risultante può essere facilmente deserializzato / analizzato da JavaScript.
Jay Taylor,

5
Perché obj potrebbe non essere un oggetto [ora, data, datetime]
Rebs,

2
il tuo esempio non è corretto se il fuso orario locale ha un offset UTC diverso da zero (la maggior parte di essi). datetime.now()restituisce l'ora locale (come oggetto datetime ingenuo) ma il codice presuppone che objsia in UTC se non è sensibile al fuso orario. Usa datetime.utcnow()invece.
jfs,

1
Modificato per generare un errore di tipo se obj non è riconosciuto secondo la raccomandazione di documentazione di Python su docs.python.org/2/library/json.html#basic-usage .
Jay Taylor,

40

Ecco la mia soluzione:

# -*- coding: utf-8 -*-
import json


class DatetimeEncoder(json.JSONEncoder):
    def default(self, obj):
        try:
            return super(DatetimeEncoder, obj).default(obj)
        except TypeError:
            return str(obj)

Quindi puoi usarlo così:

json.dumps(dictionnary, cls=DatetimeEncoder)

essere d'accordo. Molto meglio, almeno fuori dal contesto mongodb. Puoi fare isinstance(obj, datetime.datetime)all'interno di TypeError, aggiungere altri tipi da gestire e finire con str(obj)o repr(obj). E tutte le tue discariche possono semplicemente indicare questa classe specializzata.
JL Peyret, l'

@Natim questa soluzione è la migliore. +1
Souvik Ray,

20

Ho un'applicazione con un problema simile; il mio approccio era di JSONize il valore datetime come un elenco di 6 elementi (anno, mese, giorno, ora, minuti, secondi); potresti andare ai microsecondi come un elenco di 7 elementi, ma non avevo bisogno di:

class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            encoded_object = list(obj.timetuple())[0:6]
        else:
            encoded_object =json.JSONEncoder.default(self, obj)
        return encoded_object

sample = {}
sample['title'] = "String"
sample['somedate'] = datetime.datetime.now()

print sample
print json.dumps(sample, cls=DateTimeEncoder)

produce:

{'somedate': datetime.datetime(2013, 8, 1, 16, 22, 45, 890000), 'title': 'String'}
{"somedate": [2013, 8, 1, 16, 22, 45], "title": "String"}

Non funziona se il tempo risparmiato viene salvato eseguendo datetime.utcnow ()
saurshaz

1
Che errore vedi con datetime.utcnow ()? Funziona bene per me.
codingatty

17

La mia soluzione (con meno verbosità, credo):

def default(o):
    if type(o) is datetime.date or type(o) is datetime.datetime:
        return o.isoformat()

def jsondumps(o):
    return json.dumps(o, default=default)

Quindi utilizzare jsondumpsinvece di json.dumps. Stampa:

>>> jsondumps({'today': datetime.date.today()})
'{"today": "2013-07-30"}'

Vorrei, in seguito puoi aggiungere altri casi speciali a questo con una semplice modifica del defaultmetodo. Esempio:

def default(o):
    if type(o) is datetime.date or type(o) is datetime.datetime:
        return o.isoformat()
    if type(o) is decimal.Decimal:
        return float(o)

1
È necessario utilizzare isinstance (o, (datetime.date, datetime.datetime,)). Probabilmente non farebbe male includere anche datetime.time.
Rebs,

Non penso più che questa sia una buona soluzione. Probabilmente le conversioni dovrebbero prendere un posto più privilegiato - e anche un posto più comprensibile - nel tuo codice, quindi sai a cosa stai convertendo quando metti le cose in un database, o qualsiasi altra cosa, invece di fare tutto da un funzione trasparente. Ma non lo so.
fiatjaf,

1
JSON è utile per la serializzazione dei dati per l'elaborazione successiva. Potresti non sapere esattamente quali siano questi dati. E non dovresti averne bisogno. La serializzazione di JSON dovrebbe funzionare. Proprio come dovrebbe convertire unicode in ascii. L'incapacità di Python di farlo senza oscure funzioni lo rende fastidioso da usare. La convalida del database è un problema IMO separato.
Rebs,

No, non dovrebbe "funzionare". Se non sai come si è verificata la serializzazione e devi accedere ai dati in un secondo momento da un altro programma / lingua, allora sei perso.
fiatjaf,

2
JSON è comunemente usato per stringhe, ints, float, date (sono sicuro che altri usano la valuta, le temperature, anche comunemente). Ma datetime fa parte della libreria standard e dovrebbe supportare la de / serializzazione. Se non fosse per questa domanda, continuerei a cercare manualmente i miei blocchi json incredibilmente complessi (per i quali non ho sempre creato la struttura) per le date e a serializzarli 1 per 1.
Rebs

16

Questa Q si ripete più e più volte - un modo semplice per patchare il modulo json in modo tale che la serializzazione supporti il ​​datetime.

import json
import datetime

json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)

Usa la serializzazione json come fai sempre, questa volta con datetime serializzato come isoformat.

json.dumps({'created':datetime.datetime.now()})

Risultato: '{"creato": "2015-08-26T14: 21: 31.853855"}'

Vedi maggiori dettagli e alcune parole di cautela su: StackOverflow: datetime JSON tra Python e JavaScript


Patch di scimmia FTW. La cosa brutta è ovviamente che questo modifica il comportamento del modulo json in tutta la tua applicazione, il che potrebbe sorprendere gli altri in una grande applicazione, quindi dovrebbe essere generalmente usato con attenzione.
Jaap Versteegh,

15

Il metodo json.dumps può accettare un parametro opzionale chiamato default che dovrebbe essere una funzione. Ogni volta che JSON tenta di convertire un valore, non sa come convertirlo chiamerà la funzione che gli è stata passata. La funzione riceverà l'oggetto in questione e si prevede che restituisca la rappresentazione JSON dell'oggetto.

def myconverter(o):
 if isinstance(o, datetime.datetime):
    return o.__str__()

print(json.dumps(d, default = myconverter)) 

15

se stai usando python3.7, allora la soluzione migliore sta usando datetime.isoformat()e datetime.fromisoformat(); lavorano con datetimeoggetti sia ingenui che consapevoli :

#!/usr/bin/env python3.7

from datetime import datetime
from datetime import timezone
from datetime import timedelta
import json

def default(obj):
    if isinstance(obj, datetime):
        return { '_isoformat': obj.isoformat() }
    return super().default(obj)

def object_hook(obj):
    _isoformat = obj.get('_isoformat')
    if _isoformat is not None:
        return datetime.fromisoformat(_isoformat)
    return obj

if __name__ == '__main__':
    #d = { 'now': datetime(2000, 1, 1) }
    d = { 'now': datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=-8))) }
    s = json.dumps(d, default=default)
    print(s)
    print(d == json.loads(s, object_hook=object_hook))

produzione:

{"now": {"_isoformat": "2000-01-01T00:00:00-08:00"}}
True

se stai usando python3.6 o sotto, e ti interessa solo il valore del tempo (non il fuso orario), allora puoi usare datetime.timestamp()e datetime.fromtimestamp()invece;

se stai usando python3.6 o sotto, e ti preoccupi del fuso orario, allora puoi ottenerlo via datetime.tzinfo, ma devi serializzare questo campo da solo; il modo più semplice per farlo è aggiungere un altro campo _tzinfonell'oggetto serializzato;

infine, attenzione alle precisazioni in tutti questi esempi;


datetime.isoformat () è presente anche in Python 2.7: docs.python.org/2/library/…
powlo

11

È necessario utilizzare il .strftime()metodo su .datetime.now()metodo per renderlo come metodo serializzabile .

Ecco un esempio:

from datetime import datetime

time_dict = {'time': datetime.now().strftime('%Y-%m-%dT%H:%M:%S')}
sample_dict = {'a': 1, 'b': 2}
sample_dict.update(time_dict)
sample_dict

Produzione:

Out[0]: {'a': 1, 'b': 2, 'time': '2017-10-31T15:16:30'}

10

Ecco una semplice soluzione al problema "datetime not JSON serializable".

enco = lambda obj: (
    obj.isoformat()
    if isinstance(obj, datetime.datetime)
    or isinstance(obj, datetime.date)
    else None
)

json.dumps({'date': datetime.datetime.now()}, default=enco)

Output: -> {"date": "2015-12-16T04: 48: 20.024609"}


8

Devi fornire una classe di encoder personalizzata con il clsparametro di json.dumps. Per citare dai documenti :

>>> import json
>>> class ComplexEncoder(json.JSONEncoder):
...     def default(self, obj):
...         if isinstance(obj, complex):
...             return [obj.real, obj.imag]
...         return json.JSONEncoder.default(self, obj)
...
>>> dumps(2 + 1j, cls=ComplexEncoder)
'[2.0, 1.0]'
>>> ComplexEncoder().encode(2 + 1j)
'[2.0, 1.0]'
>>> list(ComplexEncoder().iterencode(2 + 1j))
['[', '2.0', ', ', '1.0', ']']

Questo usa numeri complessi come nell'esempio, ma puoi creare facilmente una classe per codificare le date (tranne che penso che JSON sia un po 'confuso sulle date)


5

Il modo più semplice per farlo è cambiare la parte del dict che è in formato datetime in isoformat. Quel valore sarà effettivamente una stringa in isoformat con cui json è d'accordo.

v_dict = version.dict()
v_dict['created_at'] = v_dict['created_at'].isoformat()

5

In realtà è abbastanza semplice. Se devi spesso serializzare le date, lavora con loro come stringhe. Se necessario, puoi facilmente riconvertirli come oggetti datetime.

Se devi lavorare principalmente come oggetti datetime, convertili come stringhe prima di serializzare.

import json, datetime

date = str(datetime.datetime.now())
print(json.dumps(date))
"2018-12-01 15:44:34.409085"
print(type(date))
<class 'str'>

datetime_obj = datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S.%f')
print(datetime_obj)
2018-12-01 15:44:34.409085
print(type(datetime_obj))
<class 'datetime.datetime'>

Come puoi vedere, l'output è lo stesso in entrambi i casi. Solo il tipo è diverso.


3

Se si sta utilizzando il risultato in una vista, assicurarsi di restituire una risposta corretta. Secondo l'API, jsonify procede come segue:

Crea una risposta con la rappresentazione JSON degli argomenti forniti con un mimetype application / json.

Per imitare questo comportamento con json.dumps è necessario aggiungere alcune righe di codice aggiuntive.

response = make_response(dumps(sample, cls=CustomEncoder))
response.headers['Content-Type'] = 'application/json'
response.headers['mimetype'] = 'application/json'
return response

Dovresti anche restituire un dict per replicare completamente la risposta di jsonify. Quindi, l'intero file sarà simile a questo

from flask import make_response
from json import JSONEncoder, dumps


class CustomEncoder(JSONEncoder):
    def default(self, obj):
        if set(['quantize', 'year']).intersection(dir(obj)):
            return str(obj)
        elif hasattr(obj, 'next'):
            return list(obj)
        return JSONEncoder.default(self, obj)

@app.route('/get_reps/', methods=['GET'])
def get_reps():
    sample = ['some text', <datetime object>, 123]
    response = make_response(dumps({'result': sample}, cls=CustomEncoder))
    response.headers['Content-Type'] = 'application/json'
    response.headers['mimetype'] = 'application/json'
    return response

1
La domanda non ha nulla a che fare con il pallone.
Zoran Pavlovic,

2
La domanda riguarda Python. La mia risposta risolve la domanda usando Python. L'OP non ha detto se la soluzione dovesse includere o escludere determinate librerie. È anche utile per chiunque legga questa domanda a cui desideri un'alternativa pymongo.
Reubano,

La loro domanda riguarda sia Python che non Flask. Flask non è nemmeno necessario nella tua risposta alla domanda, quindi ti consiglio di rimuoverlo.
Zoran Pavlovic,

3

Prova questo con un esempio per analizzarlo:

#!/usr/bin/env python

import datetime
import json

import dateutil.parser  # pip install python-dateutil


class JSONEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        return super(JSONEncoder, self).default(obj)


def test():
    dts = [
        datetime.datetime.now(),
        datetime.datetime.now(datetime.timezone(-datetime.timedelta(hours=4))),
        datetime.datetime.utcnow(),
        datetime.datetime.now(datetime.timezone.utc),
    ]
    for dt in dts:
        dt_isoformat = json.loads(json.dumps(dt, cls=JSONEncoder))
        dt_parsed = dateutil.parser.parse(dt_isoformat)
        assert dt == dt_parsed
        print(f'{dt}, {dt_isoformat}, {dt_parsed}')
        # 2018-07-22 02:22:42.910637, 2018-07-22T02:22:42.910637, 2018-07-22 02:22:42.910637
        # 2018-07-22 02:22:42.910643-04:00, 2018-07-22T02:22:42.910643-04:00, 2018-07-22 02:22:42.910643-04:00
        # 2018-07-22 06:22:42.910645, 2018-07-22T06:22:42.910645, 2018-07-22 06:22:42.910645
        # 2018-07-22 06:22:42.910646+00:00, 2018-07-22T06:22:42.910646+00:00, 2018-07-22 06:22:42.910646+00:00


if __name__ == '__main__':
    test()

2

La mia soluzione ...

from datetime import datetime
import json

from pytz import timezone
import pytz


def json_dt_serializer(obj):
    """JSON serializer, by macm.
    """
    rsp = dict()
    if isinstance(obj, datetime):
        rsp['day'] = obj.day
        rsp['hour'] = obj.hour
        rsp['microsecond'] = obj.microsecond
        rsp['minute'] = obj.minute
        rsp['month'] = obj.month
        rsp['second'] = obj.second
        rsp['year'] = obj.year
        rsp['tzinfo'] = str(obj.tzinfo)
        return rsp
    raise TypeError("Type not serializable")


def json_dt_deserialize(obj):
    """JSON deserialize from json_dt_serializer, by macm.
    """
    if isinstance(obj, str):
        obj = json.loads(obj)
    tzone = timezone(obj['tzinfo'])
    tmp_dt = datetime(obj['year'],
                      obj['month'],
                      obj['day'],
                      hour=obj['hour'],
                      minute=obj['minute'],
                      second=obj['second'],
                      microsecond=obj['microsecond'])
    loc_dt = tzone.localize(tmp_dt)
    deserialize = loc_dt.astimezone(tzone)
    return deserialize    

Ok, ora alcuni test.

# Tests
now = datetime.now(pytz.utc)

# Using this solution
rsp = json_dt_serializer(now)
tmp = json_dt_deserialize(rsp)
assert tmp == now
assert isinstance(tmp, datetime) == True
assert isinstance(now, datetime) == True

# using default from json.dumps
tmp = json.dumps(datetime.now(pytz.utc), default=json_dt_serializer)
rsp = json_dt_deserialize(tmp)
assert isinstance(rsp, datetime) == True

# Lets try another timezone
eastern = timezone('US/Eastern')
now = datetime.now(eastern)
rsp = json_dt_serializer(now)
tmp = json_dt_deserialize(rsp)

print(tmp)
# 2015-10-22 09:18:33.169302-04:00

print(now)
# 2015-10-22 09:18:33.169302-04:00

# Wow, Works!
assert tmp == now

2

Ecco la mia soluzione completa per convertire datetime in JSON e viceversa.

import calendar, datetime, json

def outputJSON(obj):
    """Default JSON serializer."""

    if isinstance(obj, datetime.datetime):
        if obj.utcoffset() is not None:
            obj = obj - obj.utcoffset()

        return obj.strftime('%Y-%m-%d %H:%M:%S.%f')
    return str(obj)

def inputJSON(obj):
    newDic = {}

    for key in obj:
        try:
            if float(key) == int(float(key)):
                newKey = int(key)
            else:
                newKey = float(key)

            newDic[newKey] = obj[key]
            continue
        except ValueError:
            pass

        try:
            newDic[str(key)] = datetime.datetime.strptime(obj[key], '%Y-%m-%d %H:%M:%S.%f')
            continue
        except TypeError:
            pass

        newDic[str(key)] = obj[key]

    return newDic

x = {'Date': datetime.datetime.utcnow(), 34: 89.9, 12.3: 90, 45: 67, 'Extra': 6}

print x

with open('my_dict.json', 'w') as fp:
    json.dump(x, fp, default=outputJSON)

with open('my_dict.json') as f:
    my_dict = json.load(f, object_hook=inputJSON)

print my_dict

Produzione

{'Date': datetime.datetime(2013, 11, 8, 2, 30, 56, 479727), 34: 89.9, 45: 67, 12.3: 90, 'Extra': 6}
{'Date': datetime.datetime(2013, 11, 8, 2, 30, 56, 479727), 34: 89.9, 45: 67, 12.3: 90, 'Extra': 6}

File JSON

{"Date": "2013-11-08 02:30:56.479727", "34": 89.9, "45": 67, "12.3": 90, "Extra": 6}

Questo mi ha permesso di importare ed esportare stringhe, ints, float e oggetti datetime. Non dovrebbe essere difficile estenderlo per altri tipi.


1
Esplode in Python 3 con TypeError: 'str' does not support the buffer interface. È a causa della 'wb'modalità aperta, dovrebbe essere 'w'. Soffia anche nella deserializzazione quando abbiamo dati simili alla data come, '0000891618-05-000338'ma non corrispondenti allo schema.
omikron,

2

Converti date in string

date = str(datetime.datetime(somedatetimehere)) 

jjmontes risponde fa esattamente questo, ma senza la necessità di farlo esplicitamente per ogni data ...
bluesummers

2

Generalmente ci sono diversi modi per serializzare i tempi dei dati, come:

  1. Stringa ISO, breve e può includere informazioni sul fuso orario, ad esempio la risposta di @ jgbarah
  2. Data / ora (dati del fuso orario persi), ad esempio la risposta di @ JayTaylor
  3. Dizionario delle proprietà (incluso il fuso orario).

Se stai bene con l'ultimo modo, il pacchetto json_tricks gestisce le date, gli orari e gli orari inclusi i fusi orari.

from datetime import datetime
from json_tricks import dumps
foo = {'title': 'String', 'datetime': datetime(2012, 8, 8, 21, 46, 24, 862000)}
dumps(foo)

che dà:

{"title": "String", "datetime": {"__datetime__": null, "year": 2012, "month": 8, "day": 8, "hour": 21, "minute": 46, "second": 24, "microsecond": 862000}}

Quindi tutto ciò che devi fare è

`pip install json_tricks`

e quindi importare json_tricksinvece di json.

Il vantaggio di non memorizzarlo come singola stringa, int o float deriva dalla decodifica: se si incontra solo una stringa o soprattutto int o float, è necessario sapere qualcosa sui dati per sapere se si tratta di un datetime. Come regola, puoi archiviare i metadati in modo che possano essere decodificati automaticamente, il che è ciò che json_tricksfa per te. È anche facilmente modificabile per gli umani.

Disclaimer: è fatto da me. Perché ho avuto lo stesso problema.


1

Ho ricevuto lo stesso messaggio di errore mentre scrivevo il decoratore serializza all'interno di una classe con sqlalchemy. Quindi invece di:

Class Puppy(Base):
    ...
    @property
    def serialize(self):
        return { 'id':self.id,
                 'date_birth':self.date_birth,
                  ...
                }

Ho semplicemente preso in prestito l'idea di jgbarah di usare isoformat () e ho aggiunto il valore originale con isoformat (), in modo che ora assomigli a:

                  ...
                 'date_birth':self.date_birth.isoformat(),
                  ...

1

Una soluzione rapida se si desidera la propria formattazione

for key,val in sample.items():
    if isinstance(val, datetime):
        sample[key] = '{:%Y-%m-%d %H:%M:%S}'.format(val) #you can add different formating here
json.dumps(sample)

1

Se ci si trova su entrambi i lati della comunicazione, è possibile utilizzare le funzioni repr () ed eval () insieme a json.

import datetime, json

dt = datetime.datetime.now()
print("This is now: {}".format(dt))

dt1 = json.dumps(repr(dt))
print("This is serialised: {}".format(dt1))

dt2 = json.loads(dt1)
print("This is loaded back from json: {}".format(dt2))

dt3 = eval(dt2)
print("This is the same object as we started: {}".format(dt3))

print("Check if they are equal: {}".format(dt == dt3))

Non dovresti importare datetime come

from datetime import datetime

poiché eval si lamenterà. Oppure puoi passare il datetime come parametro da valutare. In ogni caso dovrebbe funzionare.


0

Avevo riscontrato lo stesso problema durante l'esternalizzazione dell'oggetto modello django da scaricare come JSON. Ecco come puoi risolverlo.

def externalize(model_obj):
  keys = model_obj._meta.get_all_field_names() 
  data = {}
  for key in keys:
    if key == 'date_time':
      date_time_obj = getattr(model_obj, key)
      data[key] = date_time_obj.strftime("%A %d. %B %Y")
    else:
      data[key] = getattr(model_obj, key)
  return data

0
def j_serial(o):     # self contained
    from datetime import datetime, date
    return str(o).split('.')[0] if isinstance(o, (datetime, date)) else None

Utilizzo dell'utilità sopra:

import datetime
serial_d = j_serial(datetime.datetime.now())
if serial_d:
    print(serial_d)  # output: 2018-02-28 02:23:15

0

Questo superjson di libreria può farlo. E puoi facilmente personalizzare il serializzatore json per il tuo oggetto Python seguendo queste istruzioni https://superjson.readthedocs.io/index.html#extend .

Il concetto generale è:

il codice deve individuare il giusto metodo di serializzazione / deserializzazione basato sull'oggetto python. Di solito, il nome completo della classe è un buon identificatore.

E quindi il tuo metodo ser / deser dovrebbe essere in grado di trasformare il tuo oggetto in un normale oggetto serializzabile Json, una combinazione di tipo python generico, dict, list, string, int, float. E implementa il tuo metodo deser al contrario.


-1

Potrei non essere corretto al 100% ma, questo è il modo semplice per serializzare

#!/usr/bin/python
import datetime,json

sampledict = {}
sampledict['a'] = "some string"
sampledict['b'] = datetime.datetime.now()

print sampledict   # output : {'a': 'some string', 'b': datetime.datetime(2017, 4, 15, 5, 15, 34, 652996)}

#print json.dumps(sampledict)

'''
output : 

Traceback (most recent call last):
  File "./jsonencodedecode.py", line 10, in <module>
    print json.dumps(sampledict)
  File "/usr/lib/python2.7/json/__init__.py", line 244, in dumps
    return _default_encoder.encode(obj)
  File "/usr/lib/python2.7/json/encoder.py", line 207, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python2.7/json/encoder.py", line 270, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python2.7/json/encoder.py", line 184, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: datetime.datetime(2017, 4, 15, 5, 16, 17, 435706) is not JSON serializable


'''

sampledict['b'] = datetime.datetime.now().strftime("%B %d, %Y %H:%M %p")

afterdump = json.dumps(sampledict)

print afterdump  #output : {"a": "some string", "b": "April 15, 2017 05:18 AM"}

print type(afterdump) #<type 'str'>


afterloads = json.loads(afterdump) 

print afterloads # output : {u'a': u'some string', u'b': u'April 15, 2017 05:18 AM'}


print type(afterloads) # output :<type 'dict'> 
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.