Cum să depășiți „datetime.datetime nu este serializabil JSON”? (Programare, Python, Json)

Rolando a intrebat.

Am un dict de bază după cum urmează:

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

Când încerc să fac jsonify(sample) I get:

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

Ce pot face astfel încât exemplul meu de dict să poată depăși eroarea de mai sus?

Notă: Deși s-ar putea să nu fie relevant, dicționarele sunt generate din extragerea înregistrărilor din mongodb unde, atunci când tipăresc str(sample['somedate']), , ieșirea este 2012-08-08 21:46:24.862000.

Comentarii

  • Este acest lucru în mod specific python în general, sau, eventual, django? –  > Por jdi.
  • Din punct de vedere tehnic, este specific python, nu folosesc django, ci recuperez înregistrări din mongodb. –  > Por Rolando.
  • posibila duplicare a datei JSON între Python și JavaScript –  > Por jdi.
  • Folosesc mongoengine, dar dacă pymongo are modalități mai bune de a ocoli sau de a depăși acest lucru, vă rugăm să spuneți. –  > Por Rolando.
  • Întrebarea legată vă spune, în esență, să nu încercați să serializați obiectul datetime, ci mai degrabă să îl convertiți într-un șir de caractere în formatul ISO comun înainte de serializare. –  > Por Thomas Kelley.
31 răspunsuri
jdi

Actualizat pentru 2018

Răspunsul original a acomodat modul în care câmpurile MongoDB „date” erau reprezentate ca:

{"$date": 1506816000000}

Dacă doriți o soluție generică Python pentru serializarea datetime în json, consultați răspunsul lui @jjmontes pentru o soluție rapidă care nu necesită dependențe.


Deoarece utilizați mongoengine (conform comentariilor) și pymongo este o dependență, pymongo are utilități încorporate pentru a vă ajuta cu serializarea json:
http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

Exemplu de utilizare (serializare):

from bson import json_util
import json

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

Exemplu de utilizare (deserializare):

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

Django

Django oferă o funcție nativă DjangoJSONEncoder care se ocupă în mod corespunzător de acest tip de serializare.

A se vedea 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
)

O diferență pe care am observat-o între DjangoJSONEncoder și utilizarea unui fișier personalizat default ca aceasta:

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
)

este că Django elimină un pic din date:

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

Așa că, în unele cazuri, ar trebui să fiți atenți la asta.

Comentarii

  • Este o practică bună/nepotrivită să amestecați mai multe biblioteci, de exemplu, să aveți mongoengine pentru inserarea documentelor și pymongo pentru interogare/recuperare? –  > Por Rolando.
  • Nu este o practică proastă, doar că implică o anumită dependență față de bibliotecile pe care le folosește biblioteca principală. Dacă nu puteți realiza ceea ce aveți nevoie de la mongoengine, atunci coborâți la pymongo. Același lucru este valabil și pentru Django MongoDB. Cu cel din urmă, ați încerca să rămâneți în cadrul ORM django pentru a menține starea agnostică a backend-ului. Dar, uneori, nu poți realiza ceea ce ai nevoie în abstracție, așa că cobori un nivel. În acest caz, nu are nicio legătură cu problema dvs., deoarece folosiți doar metode de utilitate pentru a însoți formatul JSON. –  > Por jdi.
  • Încerc acest lucru cu Flask și se pare că, folosind json.dump, nu pot să pun un înveliș jsonify() în jurul acestuia astfel încât să se întoarcă în application/json. Încercarea de a face return jsonify(json.dumps(sample, default=json_util.default)) – –  > Por Rolando.
  • @amit Nu este vorba atât de mult despre memorarea sintaxei, cât despre a deveni bun la citirea documentației și la stocarea a suficiente informații în capul meu pentru a recunoaște unde și când trebuie să le recuperez din nou. În acest caz, cineva ar putea spune „Oh, un obiect personalizat cu json” și apoi să reîmprospăteze rapid această utilizare –  > Por jdi.
  • @guyskk Nu am urmărit modificările în bjson sau mongo de când am scris asta acum 5 ani. Dar dacă doriți să aveți control asupra serializării datei, atunci trebuie să vă scrieți propria funcție handler implicită, așa cum este ilustrat în răspunsul dat de jgbarah –  > Por jdi.
jjmontes

Meu rapid & murdar JSON dump care mănâncă date și totul:

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

default este o funcție aplicată obiectelor care nu sunt serializabile.
În acest caz este str, așa că doar convertește tot ce nu știe în șiruri de caractere. Ceea ce este grozav pentru serializare, dar nu atât de grozav atunci când deserializează (de aici „quick & dirty”), deoarece orice ar fi putut fi stringificat fără avertisment, de exemplu o funcție sau o matrice numpy.

Comentarii

    18

  • Acest lucru este minunat, Dar, din păcate, nu am înțeles ce s-a întâmplat? Poate cineva să explice acest răspuns? –  > Por Kishor Pawar.
  • 84

  • @KishorPawar: default este o funcție care se aplică obiectelor care nu sunt serializabile. În acest caz este str, , așa că doar convertește tot ceea ce nu știe în șiruri de caractere. Ceea ce este grozav pentru serializare, dar nu atât de grozav atunci când se deserializează (de aici „quick & dirty”), deoarece orice ar fi putut fi stringificat fără avertisment, de exemplu o funcție sau o matrice numpy. –  > Por Mark.
  • @Mark minunat. Mulțumesc. Util atunci când cunoști tipul acelor valori neserializabile, cum ar fi datele. –  > Por Kishor Pawar.
  • 17

  • De ce am mers toată viața mea fără să știu acest lucru. 🙂 –  > Por Arel.
  • @jjmontes, nu funcționează pentru orice, de ex. json.dumps({():1,type(None):2},default=str) ridică TypeError, , nu poate avea tip sau tuple. –  > Por alancalvitti.
jgbarah

Pornind de la alte răspunsuri, o soluție simplă bazată pe un serializator specific care doar convertește datetime.datetime și datetime.date obiecte în șiruri de caractere.

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))

După cum se vede, codul verifică doar dacă obiectul este din clasa datetime.datetime sau datetime.date, și apoi folosește .isoformat() pentru a produce o versiune serializată a acestuia, în conformitate cu formatul ISO 8601, YYYY-MM-DDTHH:MM:SS (care este ușor de decodificat de JavaScript). În cazul în care se caută reprezentări serializate mai complexe, se poate utiliza alt cod în locul lui str() (a se vedea alte răspunsuri la această întrebare pentru exemple). Codul se încheie prin ridicarea unei excepții, pentru a face față cazului în care este apelat cu un tip neserializabil.

Această funcție json_serial poate fi utilizată după cum urmează:

from datetime import datetime
from json import dumps

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

Detaliile despre modul în care funcționează parametrul implicit pentru json.dumps pot fi găsite în secțiunea Basic Usage din documentația modulului json.

Comentarii

  • Da, răspunsul corect, mai degrabă import datetime și if isinstance(obj, datetime.datetime) , am pierdut mult timp pentru că nu am folosit from datetime import datetime , oricum mulțumesc –  > Por Sérgio.
  • 15

  • dar acest lucru nu explică cum să îl deserializăm cu tipul corect, nu-i așa? –  > Por BlueTrin.
  • Nu, @BlueTrin , nu se spune nimic despre asta. În cazul meu, deserializez în JavaScript, ceea ce funcționează din start. –  > Por jgbarah.
  • Acest lucru va cauza un comportament neașteptat dacă modulul json se va actualiza vreodată pentru a include serializarea obiectelor datetime. –  > Por Justin.
  • @serg Dar convertirea orelor în UTC ar unifica 01:00:00+01:00 și 02:00:00+00:00 care nu ar trebui să fie aceleași, în funcție de context. Ele se referă la același punct în timp, desigur, dar decalajul ar putea fi un aspect relevant al valorii. –  > Por Alfe.
lenny

Tocmai m-am confruntat cu această problemă și soluția mea este de a subclasa 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)

În apelul dvs. faceți ceva de genul: json.dumps(yourobj, cls=DateTimeEncoder) The .isoformat() Am luat de la unul dintre răspunsurile de mai sus.

Comentarii

    23

  • a fost ridicată, deoarece implementarea unui JSONEncoder personalizat ar trebui să fie calea corectă de urmat –  > Por 3k-.
  • 27

  • Nu numai că acesta ar trebui să fie răspunsul de top, dar ar trebui să facă parte din codificatorul json obișnuit. Dacă decodarea ar fi mai puțin ambiguă… – –  > Por Joost.
  • Pentru cei care folosesc Django, consultați DjangoJSONEncoder. docs.djangoproject.com/en/dev/topics/serialization/… –  > Por S. Kirby.
  • Super util. Ultima linie ar putea fi return super(DateTimeEncoder, self).default(o) –  > Por Bob Stein.
  • 18

  • Cu Python 3, ultima linie este chiar mai simplă: return super().default(o) –  > Por ariddell.
D.A

Convertiți data într-un șir de caractere

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

Comentarii

  • Și cum aș putea să o deserializez în Python? –  > Por wobmene.
  • 66

  • Problema este dacă aveți multe obiecte datetime încorporate adânc într-o structură de date sau dacă acestea sunt aleatorii. Aceasta nu este o metodă fiabilă. –  > Por Rebs.
  • pentru a deserializa: oDate = datetime.datetime.strptime(sDate, '%Y-%m-%d %H:%M:%S.%f'). Formatele obținute din: docs.python.org/2/library/datetime.html –  > Por Roman.
  • Downvoted deoarece ignoră informațiile despre fusul orar. Țineți cont de faptul că .now() folosește ora locală, fără a indica acest lucru. Cel puțin .utcnow() ar trebui să fie folosit (și apoi să se adauge un +0000 sau un Z) –  > Por Daniel F.
  • @DanielF At least .utcnow() should be used Nu chiar, datetime.now(timezone.utc) este recomandat, vezi avertismentul din: docs.python.org/3.8/library/…. –  > Por Toreno96.
Jay Taylor

Pentru alții care nu au nevoie sau nu doresc să utilizeze biblioteca pymongo pentru acest lucru… puteți realiza cu ușurință conversia JSON datatime cu acest mic fragment:

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,))

Apoi, utilizați-l astfel:

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

ieșire: 

'1365091796124'

Comentarii

  • Nu ar trebui să millis= fi indentat în interiorul declarației if? De asemenea, este probabil mai bine să folosiți str(obj) pentru a obține formatul ISO, care cred că este mai comun. –  > Por Rebs.
  • De ce ai vrea să fie indentat? Acest fragment funcționează, iar rezultatul poate fi ușor de deserializat/parazitat din javascript. –  > Por Jay Taylor.
  • Deoarece obj poate să nu fie un obiect [time, date, datetime] –  > Por Rebs.
  • exemplul dvs. este incorect dacă fusul orar local are un decalaj UTC diferit de zero (majoritatea). datetime.now() returnează ora locală (ca un obiect naiv datetime), dar codul dvs. presupune că obj este în UTC dacă nu este conștient de fusul orar. Utilizați datetime.utcnow() în schimb. –  > Por jfs.
  • Ajustat pentru a genera o eroare de tip dacă obj este nerecunoscut, conform recomandării din documentația Python la adresa docs.python.org/2/library/json.html#basic-usage. –  > Por Jay Taylor.
Natim

Iată soluția mea:

import json


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

Atunci îl poți folosi așa:

json.dumps(dictionnary, cls=DatetimeEncoder)

Comentarii

  • de acord. Mult mai bine, cel puțin în afara contextului mongodb. Puteți face isinstance(obj, datetime.datetime) în cadrul TypeError, să adăugați mai multe tipuri de gestionat și să terminați cu str(obj) sau repr(obj). Și toate descărcările tale pot indica doar această clasă specializată. –  > Por JL Peyret.
  • @Natim această soluție este cea mai bună. +1 –  > Por Souvik Ray.
  • Cum rămâne cu decodarea? –  > Por Thomas Sauvajon.
  • @ThomasSauvajon stackoverflow.com/a/40489783/186202 –  > Por Natim.
Cyker

dacă folosiți python3.7, atunci cea mai bună soluție este să folosițidatetime.isoformat() șidatetime.fromisoformat(); acestea funcționează atât cu naive andaware cât și cu datetime și cu obiectele naive:

#!/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))

ieșire:

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

dacă utilizați python3.6 sau o versiune mai mică și vă interesează doar valoarea timpului (nu și fusul orar), puteți utiliza datetime.timestamp() șidatetime.fromtimestamp() în schimb;

dacă folosiți python3.6 sau o versiune ulterioară și vă interesează fusul orar, atunci îl puteți obține prin datetime.tzinfo, , dar trebuie să serializați acest câmp; cel mai simplu mod de a face acest lucru este să adăugați un alt câmp _tzinfo în obiectul serializat;

în cele din urmă, aveți grijă la precizările din toate aceste exemple;

Comentarii

Saurabh Saha

Metoda json.dumps poate accepta un parametru opțional numit default, care se așteaptă să fie o funcție. De fiecare dată când JSON încearcă să convertească o valoare pe care nu știe cum să o convertească, va apela funcția pe care i-am transmis-o. Funcția va primi obiectul în cauză și se așteaptă ca aceasta să returneze reprezentarea JSON a obiectului.

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

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

codingatty

Am o aplicație cu o problemă similară; abordarea mea a fost să JSONizez valoarea datetime ca o listă de 6 elemente (an, lună, zi, oră, minute, secunde); ați putea trece la microsecunde ca o listă de 7 elemente, dar nu am avut nevoie:

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"}

Comentarii

  • Nu reușește să funcționeze dacă timpul salvat este salvat făcând datetime.utcnow() –  > Por saurshaz.
  • Ce eroare vezi cu datetime.utcnow()? Pentru mine funcționează bine. –  > Por codingatty.
fiatjaf

Soluția mea (cu mai puțină verbozitate, cred):

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)

Apoi utilizați jsondumps în loc de json.dumps. Se va imprima:

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

Dacă doriți, mai târziu puteți adăuga și alte cazuri speciale la aceasta cu o simplă răsucire a default metodă. Exemplu:

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)

Comentarii

  • Ar trebui să utilizați isinstance(o, (datetime.date, datetime.datetime,)). Probabil că nu ar strica să includeți și datetime.time. –  > Por Rebs.
  • Nu cred că aceasta mai este o soluție bună. Probabil că conversiile ar trebui să ocupe un loc mai privilegiat – și, de asemenea, un loc mai ușor de înțeles – în cod, astfel încât să știi în ce convertești atunci când introduci lucrurile într-o bază de date sau orice altceva, în loc ca totul să fie făcut de o funcție transparentă. Dar nu știu. –  > Por fiatjaf.
  • JSON este bun pentru serializarea datelor în vederea prelucrării ulterioare. S-ar putea să nu știi exact ce sunt acele date. Și nu ar trebui să fie nevoie să știți. Serializarea JSON ar trebui să funcționeze pur și simplu. La fel cum ar trebui să funcționeze și conversia Unicode în ascii. Incapacitatea lui Python de a face acest lucru fără funcții obscure îl face enervant de utilizat. Validarea bazei de date este o problemă separată, IMO. –  > Por Rebs.
  • Nu, nu ar trebui să „funcționeze pur și simplu”. Dacă nu știi cum a avut loc serializarea și trebuie să accesezi datele mai târziu dintr-un alt program/limbaj, atunci ești pierdut. –  > Por fiatjaf.
  • JSON este folosit în mod obișnuit pentru șiruri de caractere, ints, float, date (sunt sigur că și alții folosesc în mod obișnuit monedă, temperaturi, de asemenea). Dar datetime face parte din biblioteca standard și ar trebui să suporte de/serializare. Dacă nu ar fi fost această întrebare, încă aș fi căutat manual în blob-urile mele json incredibil de complexe (pentru care nu am creat întotdeauna structura) pentru date și le-aș fi serializat 1 câte 1. –  > Por Rebs.
davidhadas

Această întrebare se repetă mereu și mereu – o modalitate simplă de a corecta modulul json astfel încât serializarea să suporte datetime.

import json
import datetime

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

Decât să folosiți serializarea json așa cum o faceți întotdeauna – de data aceasta cu datatime fiind serializat ca isoformat.

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

Rezultatul este: ‘{„created”: „2015-08-26T14:21:31.853855”}’

Vedeți mai multe detalii și câteva cuvinte de precauție la:StackOverflow: JSON datetime între Python și JavaScript

Comentarii

  • Patch de maimuță FTW. Lucrul neplăcut este, desigur, că acest lucru modifică comportamentul modulului json în întreaga dvs. aplicație, ceea ce poate surprinde pe alții într-o aplicație mare, așa că ar trebui, în general, să fie folosit cu grijă imho. –  > Por Jaap Versteegh.
Benyamin Jafari

Ar trebui să utilizați .strftime() metoda pe .datetime.now() pentru a o face ca o metodă serializabil metodă.

Iată un exemplu:

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

Output:

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

ob92

Iată o soluție simplă pentru a depăși 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)

Ieșire: -> {„date”: „2015-12-16T04:48:20.024609”}

Sean Redmond

Trebuie să furnizați o clasă de codificator personalizată cu cls parametru de json.dumps. Pentru a cita din docs:

>>> 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', ']']

Acesta folosește numere complexe ca exemplu, dar puteți crea la fel de ușor o clasă pentru a codifica date (doar că JSON este puțin confuz în ceea ce privește datele).

Peter Graham

Cel mai simplu mod de a face acest lucru este de a schimba partea din dict care este în format datetime în izoformat. Această valoare va fi efectiv un șir de caractere în isoformat, ceea ce este în regulă pentru JSON.

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

AngelDown

De fapt, este destul de simplu. dacă aveți nevoie să serializați des datele, atunci lucrați cu ele ca șiruri. Le puteți converti cu ușurință înapoi ca obiecte datetime dacă este necesar.

Dacă trebuie să lucrați mai ales ca obiecte datetime, atunci convertiți-le ca șiruri înainte de serializare.

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'>

După cum puteți vedea, rezultatul este același în ambele cazuri. Doar tipul este diferit.

zhigang

Încercați aceasta cu un exemplu pentru a o analiza:

#!/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()

reubano

Dacă utilizați rezultatul într-o vizualizare, asigurați-vă că returnează un răspuns corespunzător. Conform API-ului, jsonify face următoarele:

Creează un răspuns cu reprezentarea JSON a argumentelor date cu un mimetype application/json.

Pentru a imita acest comportament cu json.dumps trebuie să adăugați câteva linii de cod suplimentare.

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

De asemenea, ar trebui să returnați un dict pentru a reproduce complet răspunsul lui jsonify. Așadar, întregul fișier va arăta astfel

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

Comentarii

  • Întrebarea nu are nimic de-a face cu flask. –  > Por Zoran Pavlovic.
  • Întrebarea se referă la python. Răspunsul meu rezolvă întrebarea folosind python. OP nu a spus dacă soluția ar trebui să includă sau să excludă anumite biblioteci. De asemenea, este util pentru oricine altcineva care citește această întrebare și dorește o alternativă la pymongo. –  > Por reubano.
  • Întrebarea se referă atât la Python, cât și la nu despre Flask. Flask nici măcar nu este necesar în răspunsul tău la întrebare, așa că îți sugerez să îl elimini. –  > Por Zoran Pavlovic.
  • În flask este mult mai ușor de utilizat flask.json.dumps se gestionează obiectele datetime. –  > Por Jonatan.
macm

Soluția mea …

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, acum câteva teste.

# 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

Hovo

Aici este soluția mea completă pentru a converti datetime în JSON și înapoi…

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

Ieșire

{'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}

Fișier JSON

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

Acest lucru mi-a permis să importe și să exporte șiruri de caractere, ints, float și obiecte datetime.It nu ar trebui să fie greu de extins pentru alte tipuri.

Comentarii

  • Acesta explodează în Python 3 cu TypeError: 'str' does not support the buffer interface. Acest lucru se datorează 'wb' modul deschis, ar trebui să fie 'w'. De asemenea, explodează și în deserializare când avem date similare cu data, cum ar fi '0000891618-05-000338' dar nu se potrivesc cu modelul. –  > Por omikron.
Rana Nematollahi

Conversia date în string

date = str(datetime.datetime(somedatetimehere)) 

Comentarii

  • Răspunsul jjmontes face exact acest lucru, dar fără a fi nevoie să o faceți în mod explicit pentru fiecare dată… –  > Por bluesummers.
Mark

În general, există mai multe moduri de a serializa datetime, cum ar fi:

  1. Șir ISO, scurt și poate include informații despre fusul orar, de exemplu, răspunsul lui @jgbarah
  2. Timestamp (datele de fus orar se pierd), de exemplu, răspunsul lui @JayTaylor
  3. Dicționar de proprietăți (inclusiv fusul orar).

Dacă sunteți de acord cu ultima modalitate, opțiunea json_tricks se ocupă de date, ore și date-timpuri, inclusiv de fus orar.

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

ceea ce dă:

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

Așadar, tot ce trebuie să faceți este

`pip install json_tricks`

și apoi să importați din json_tricks în loc de json.

Avantajul de a nu stoca un singur șir de caractere, int sau float apare la decodare: dacă întâlniți doar un șir de caractere sau mai ales int sau float, trebuie să știți ceva despre date pentru a ști dacă este o dată. Ca un dict, puteți stoca metadate astfel încât să poată fi decodificat automat, ceea ce este ceea ce json_tricks face pentru dumneavoastră. De asemenea, este ușor de editat pentru oameni.

Disclaimer: este făcut de mine. Pentru că am avut aceeași problemă.

Treefish Zhang

Am primit același mesaj de eroare în timp ce scriam decoratorul serialize în interiorul unei clase cu sqlalchemy. Deci, în loc de :

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

am împrumutat pur și simplu ideea lui jgbarah de a folosi isoformat() și am adăugat valoarea originală cu isoformat(), astfel încât acum arată așa::

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

Arash

O rezolvare rapidă dacă doriți o formatare proprie

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)

ThunderBear

Dacă vă aflați de ambele părți ale comunicării, puteți folosi repr() și eval() împreună cu funcțiile 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))

Nu ar trebui să importați datetime ca

from datetime import datetime

deoarece eval se va plânge. Sau puteți trece datetime ca parametru pentru eval. În orice caz, acest lucru ar trebui să funcționeze.

naren

Am întâlnit aceeași problemă atunci când externalizam obiectul modelului django pentru a-l descărca ca JSON.Iată cum o puteți rezolva.

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

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

Utilizarea utilitarului de mai sus:

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

MacSanhe

Această bibliotecă superjson poate face acest lucru. Și puteți personaliza cu ușurință serializatorul json pentru propriul obiect Python urmând această instrucțiune https://superjson.readthedocs.io/index.html#extend.

Conceptul general este:

codul dvs. trebuie să localizeze metoda corectă de serializare / deserializare pe baza obiectului python. De obicei, numele complet al clasei este un bun identificator.

Și apoi metoda dvs. de serizare / deserizare ar trebui să fie capabilă să transforme obiectul dvs. într-un obiect Json serializabil obișnuit, o combinație de tip generic python, dict, list, string, int, float. Și să implementezi metoda deser în sens invers.

Appaji Chintimi

M-am confruntat cu această problemă astăzi, am găsit ceva numit pickle. Este o bibliotecă încorporată pentru serializarea obiectelor python și, de asemenea, încărcați-o dintr-un fișier pickle.

Singura diferență pe care am găsit-o între pickle și json este pickle este un fișier binar, în timp ce json este un fișier text obișnuit.

Și nu cauzează nicio problemă cu obiectele datetime.

Shiva

S-ar putea să nu fiu 100% corect, dar acesta este modul simplu de a face serializarea

#!/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'> 

Tags:,