Voglio inviare un oggetto datetime.datetime in forma serializzata da Python usando JSON e deserializzare in JavaScript usando JSON. Qual è il modo migliore per farlo?
Voglio inviare un oggetto datetime.datetime in forma serializzata da Python usando JSON e deserializzare in JavaScript usando JSON. Qual è il modo migliore per farlo?
Risposte:
È possibile aggiungere il parametro 'default' a json.dumps per gestire questo:
date_handler = lambda obj: (
obj.isoformat()
if isinstance(obj, (datetime.datetime, datetime.date))
else None
)
json.dumps(datetime.datetime.now(), default=date_handler)
'"2010-04-20T20:08:21.634121"'
Che è il formato ISO 8601 .
Una funzione di gestione predefinita più completa:
def handler(obj):
if hasattr(obj, 'isoformat'):
return obj.isoformat()
elif isinstance(obj, ...):
return ...
else:
raise TypeError, 'Object of type %s with value of %s is not JSON serializable' % (type(obj), repr(obj))
Aggiornamento: aggiunto l'output di tipo e valore.
Aggiornamento: gestire anche la data
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
Per i progetti in più lingue, ho scoperto che le stringhe contenenti date RfC 3339 sono il modo migliore per procedere. Una data RfC 3339 si presenta così:
1985-04-12T23:20:50.52Z
Penso che la maggior parte del formato sia ovvio. L'unica cosa in qualche modo insolita potrebbe essere la "Z" alla fine. Sta per GMT / UTC. Puoi anche aggiungere un fuso orario come +02: 00 per CEST (Germania in estate). Personalmente preferisco tenere tutto in UTC fino a quando non viene visualizzato.
Per la visualizzazione, i confronti e l'archiviazione è possibile lasciarlo in formato stringa in tutte le lingue. Se è necessaria la data per i calcoli, è possibile riconvertirla in un oggetto data nativo nella maggior parte delle lingue.
Quindi genera il JSON in questo modo:
json.dump(datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))
Sfortunatamente, il costruttore Date di Javascript non accetta le stringhe RfC 3339 ma ci sono molti parser disponibili su Internet.
huTools.hujson tenta di gestire i problemi di codifica più comuni che potresti riscontrare nel codice Python, inclusi gli oggetti data / datetime, gestendo correttamente i fusi orari.
datetime
: datetime.isoformat () che da simplejson
, che scaricherà gli datetime
oggetti come isoformat
stringhe per impostazione predefinita. Non c'è bisogno di strftime
hacking manuale .
datetime
oggetti alla isoformat
stringa. Per me, simplejson.dumps(datetime.now())
reseTypeError: datetime.datetime(...) is not JSON serializable
json.dumps(datetime.datetime.now().isoformat())
è dove accade la magia.
L'ho capito.
Supponiamo che tu abbia un oggetto datetime Python, d , creato con datetime.now (). Il suo valore è:
datetime.datetime(2011, 5, 25, 13, 34, 5, 787000)
È possibile serializzarlo su JSON come stringa datetime ISO 8601:
import json
json.dumps(d.isoformat())
L'oggetto datetime di esempio verrebbe serializzato come:
'"2011-05-25T13:34:05.787000"'
Questo valore, una volta ricevuto nel livello Javascript, può costruire un oggetto Date:
var d = new Date("2011-05-25T13:34:05.787000");
A partire da Javascript 1.8.5, gli oggetti Date hanno un metodo toJSON, che restituisce una stringa in un formato standard. Per serializzare l'oggetto Javascript sopra riportato su JSON, pertanto, il comando sarebbe:
d.toJSON()
Che ti darebbe:
'2011-05-25T20:34:05.787Z'
Questa stringa, una volta ricevuta in Python, potrebbe essere nuovamente deserializzata in un oggetto datetime:
datetime.strptime('2011-05-25T20:34:05.787Z', '%Y-%m-%dT%H:%M:%S.%fZ')
Ciò si traduce nel seguente oggetto datetime, che è lo stesso con cui hai iniziato e quindi corretto:
datetime.datetime(2011, 5, 25, 20, 34, 5, 787000)
Usando json
, puoi sottoclassare JSONEncoder e sovrascrivere il metodo default () per fornire i tuoi serializzatori personalizzati:
import json
import datetime
class DateTimeJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
else:
return super(DateTimeJSONEncoder, self).default(obj)
Quindi, puoi chiamarlo così:
>>> DateTimeJSONEncoder().encode([datetime.datetime.now()])
'["2010-06-15T14:42:28"]'
obj.isoformat()
. Puoi anche usare la dumps()
chiamata più comune , che accetta altri argomenti utili (come indent
): simplejson.dumps (myobj, cls = JSONEncoder, ...)
Ecco una soluzione abbastanza completa per codificare e decodificare ricorsivamente oggetti datetime.datetime e datetime.date usando il json
modulo libreria standard . Ciò richiede Python> = 2.6 poiché il %f
codice di formato nella stringa di formato datetime.datetime.strptime () è supportato solo da allora. Per il supporto di Python 2.5, elimina %f
e rimuovi i microsecondi dalla stringa della data ISO prima di provare a convertirla, ma perderai la precisione dei microsecondi, ovviamente. Per l'interoperabilità con stringhe di date ISO di altre fonti, che possono includere un nome di fuso orario o un offset UTC, potrebbe essere necessario rimuovere alcune parti della stringa di date prima della conversione. Per un parser completo per stringhe di date ISO (e molti altri formati di data) consultare il modulo dateutil di terze parti .
La decodifica funziona solo quando le stringhe di date ISO sono valori in una notazione di oggetto letterale JavaScript o in strutture nidificate all'interno di un oggetto. Le stringhe di date ISO, che sono elementi di un array di livello superiore non verranno decodificate.
Cioè funziona:
date = datetime.datetime.now()
>>> json = dumps(dict(foo='bar', innerdict=dict(date=date)))
>>> json
'{"innerdict": {"date": "2010-07-15T13:16:38.365579"}, "foo": "bar"}'
>>> loads(json)
{u'innerdict': {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)},
u'foo': u'bar'}
E anche questo:
>>> json = dumps(['foo', 'bar', dict(date=date)])
>>> json
'["foo", "bar", {"date": "2010-07-15T13:16:38.365579"}]'
>>> loads(json)
[u'foo', u'bar', {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)}]
Ma questo non funziona come previsto:
>>> json = dumps(['foo', 'bar', date])
>>> json
'["foo", "bar", "2010-07-15T13:16:38.365579"]'
>>> loads(json)
[u'foo', u'bar', u'2010-07-15T13:16:38.365579']
Ecco il codice:
__all__ = ['dumps', 'loads']
import datetime
try:
import json
except ImportError:
import simplejson as json
class JSONDateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime.date, datetime.datetime)):
return obj.isoformat()
else:
return json.JSONEncoder.default(self, obj)
def datetime_decoder(d):
if isinstance(d, list):
pairs = enumerate(d)
elif isinstance(d, dict):
pairs = d.items()
result = []
for k,v in pairs:
if isinstance(v, basestring):
try:
# The %f format code is only supported in Python >= 2.6.
# For Python <= 2.5 strip off microseconds
# v = datetime.datetime.strptime(v.rsplit('.', 1)[0],
# '%Y-%m-%dT%H:%M:%S')
v = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S.%f')
except ValueError:
try:
v = datetime.datetime.strptime(v, '%Y-%m-%d').date()
except ValueError:
pass
elif isinstance(v, (dict, list)):
v = datetime_decoder(v)
result.append((k, v))
if isinstance(d, list):
return [x[1] for x in result]
elif isinstance(d, dict):
return dict(result)
def dumps(obj):
return json.dumps(obj, cls=JSONDateTimeEncoder)
def loads(obj):
return json.loads(obj, object_hook=datetime_decoder)
if __name__ == '__main__':
mytimestamp = datetime.datetime.utcnow()
mydate = datetime.date.today()
data = dict(
foo = 42,
bar = [mytimestamp, mydate],
date = mydate,
timestamp = mytimestamp,
struct = dict(
date2 = mydate,
timestamp2 = mytimestamp
)
)
print repr(data)
jsonstring = dumps(data)
print jsonstring
print repr(loads(jsonstring))
datetime.datetime.utcnow().isoformat()[:-3]+"Z"
sarà esattamente come quella prodotta da JSON.stringify () in javascript
Se sei sicuro che solo Javascript utilizzerà il JSON, preferisco passare Date
direttamente gli oggetti Javascript .
Il ctime()
metodo sugli datetime
oggetti restituirà una stringa che l'oggetto Javascript Date può comprendere.
import datetime
date = datetime.datetime.today()
json = '{"mydate":new Date("%s")}' % date.ctime()
Javascript lo utilizzerà felicemente come un oggetto letterale e hai incorporato l'oggetto Date.
.ctime()
è un modo MOLTO cattivo per passare le informazioni sul tempo, .isoformat()
è molto meglio. Ciò che .ctime()
fa è buttare via il fuso orario e l'ora legale come se non esistessero. Quella funzione dovrebbe essere uccisa.
Verso la fine del gioco ... :)
Una soluzione molto semplice è patchare l'impostazione predefinita del modulo json. Per esempio:
import json
import datetime
json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)
Ora puoi usare json.dumps () come se avesse sempre supportato datetime ...
json.dumps({'created':datetime.datetime.now()})
Ciò ha senso se si richiede che questa estensione al modulo json si avvii sempre e si desideri non cambiare il modo in cui l'utente o gli altri utilizzano la serializzazione json (nel codice esistente o meno).
Si noti che alcuni potrebbero considerare l'applicazione di patch alle librerie in quel modo come cattiva pratica. È necessario prestare particolare attenzione nel caso in cui si desideri estendere l'applicazione in più di un modo: in tal caso, suggerisco di utilizzare la soluzione di ramen o JT e scegliere l'estensione json appropriata in ciascun caso.
None
. È possibile che si desideri generare un'eccezione.
Non c'è molto da aggiungere alla risposta wiki della community, ad eccezione del timestamp !
Javascript utilizza il seguente formato:
new Date().toJSON() // "2016-01-08T19:00:00.123Z"
Lato Python (per il json.dumps
gestore, vedi le altre risposte):
>>> from datetime import datetime
>>> d = datetime.strptime('2016-01-08T19:00:00.123Z', '%Y-%m-%dT%H:%M:%S.%fZ')
>>> d
datetime.datetime(2016, 1, 8, 19, 0, 0, 123000)
>>> d.isoformat() + 'Z'
'2016-01-08T19:00:00.123000Z'
Se si lascia fuori quella Z, i framework frontend come angular non possono visualizzare la data nel fuso orario locale del browser:
> $filter('date')('2016-01-08T19:00:00.123000Z', 'yyyy-MM-dd HH:mm:ss')
"2016-01-08 20:00:00"
> $filter('date')('2016-01-08T19:00:00.123000', 'yyyy-MM-dd HH:mm:ss')
"2016-01-08 19:00:00"
Il mio consiglio è di usare una biblioteca. Ce ne sono diversi disponibili su pypi.org.
Lo uso, funziona bene: https://pypi.python.org/pypi/asjson
Apparentemente il formato della data "giusto" JSON (bene JavaScript) è 23/04/2012 T18: 25: 43.511Z - UTC e "Z". Senza questo JavaScript verrà utilizzato il fuso orario locale del browser Web durante la creazione di un oggetto Date () dalla stringa.
Per un tempo "ingenuo" (ciò che Python chiama un orario senza fuso orario e questo presuppone che sia locale) il seguente forzerà il fuso orario locale in modo che possa essere correttamente convertito in UTC:
def default(obj):
if hasattr(obj, "json") and callable(getattr(obj, "json")):
return obj.json()
if hasattr(obj, "isoformat") and callable(getattr(obj, "isoformat")):
# date/time objects
if not obj.utcoffset():
# add local timezone to "naive" local time
# /programming/2720319/python-figure-out-local-timezone
tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
obj = obj.replace(tzinfo=tzinfo)
# convert to UTC
obj = obj.astimezone(timezone.utc)
# strip the UTC offset
obj = obj.replace(tzinfo=None)
return obj.isoformat() + "Z"
elif hasattr(obj, "__str__") and callable(getattr(obj, "__str__")):
return str(obj)
else:
print("obj:", obj)
raise TypeError(obj)
def dump(j, io):
json.dump(j, io, indent=2, default=default)
Perche'e'cosi difficile.
Per la conversione della data da Python a JavaScript, l'oggetto data deve essere in formato ISO specifico, ovvero formato ISO o numero UNIX. Se nel formato ISO mancano alcune informazioni, puoi prima convertirlo nel numero Unix con Date.parse. Inoltre, Date.parse funziona anche con React mentre la nuova Data potrebbe attivare un'eccezione.
Nel caso in cui si disponga di un oggetto DateTime senza millisecondi, è necessario considerare quanto segue. :
var unixDate = Date.parse('2016-01-08T19:00:00')
var desiredDate = new Date(unixDate).toLocaleDateString();
La data di esempio potrebbe ugualmente essere una variabile nell'oggetto result.data dopo una chiamata API.
Per le opzioni per visualizzare la data nel formato desiderato (ad es. Per visualizzare i giorni feriali lunghi), consultare il documento MDN .