Wie kann man "datetime.datetime nicht JSON serialisierbar" überwinden?


742

Ich habe ein grundlegendes Diktat wie folgt:

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

Wenn ich es versuche, jsonify(sample)bekomme ich:

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

Was kann ich tun, damit mein Wörterbuchbeispiel den oben genannten Fehler beheben kann?

Hinweis: Obwohl dies möglicherweise nicht relevant ist, werden die Wörterbücher aus dem Abrufen von Datensätzen generiert, aus mongodbdenen beim Ausdrucken str(sample['somedate'])die Ausgabe erfolgt 2012-08-08 21:46:24.862000.


1
Ist das speziell Python im Allgemeinen oder möglicherweise Django?
JDI

1
Es ist technisch gesehen speziell Python, ich benutze kein Django, sondern rufe Datensätze aus Mongodb ab.
Rolando


Ich benutze Mongoengine, aber wenn Pymongo bessere Möglichkeiten hat, dies zu umgehen oder zu überwinden, sagen Sie es bitte.
Rolando

3
Die verknüpfte Frage besagt im Wesentlichen, dass Sie nicht versuchen sollen, das datetime-Objekt zu serialisieren, sondern es vor der Serialisierung in eine Zeichenfolge im allgemeinen ISO-Format zu konvertieren.
Thomas Kelley

Antworten:


377

Aktualisiert für 2018

Die ursprüngliche Antwort entsprach der Darstellung der MongoDB-Datumsfelder als:

{"$date": 1506816000000}

Wenn Sie eine generische Python-Lösung für die Serialisierung datetimenach json wünschen , lesen Sie die Antwort von @jjmontes, um eine schnelle Lösung zu finden, für die keine Abhängigkeiten erforderlich sind.


Da Sie Mongoengine verwenden (laut Kommentaren) und Pymongo eine Abhängigkeit ist, verfügt Pymongo über integrierte Dienstprogramme, die bei der JSON-Serialisierung helfen:
http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

Anwendungsbeispiel (Serialisierung):

from bson import json_util
import json

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

Anwendungsbeispiel (Deserialisierung):

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

Django

Django bietet einen nativen DjangoJSONEncoderSerializer, der diese Art von richtig behandelt.

Siehe https://docs.djangoproject.com/de/dev/topics/serialization/#djangojsonencoder

from django.core.serializers.json import DjangoJSONEncoder

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

Ein Unterschied, den ich zwischen der DjangoJSONEncoderVerwendung eines solchen Brauchs festgestellt habe default:

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
)

Ist das, dass Django ein bisschen von den Daten entfernt:

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

In einigen Fällen müssen Sie daher vorsichtig sein.


3
Ist es eine gute / schlechte Praxis, mehrere Bibliotheken zu mischen, dh eine Mongoengine zum Einfügen von Dokumenten und einen Pymongo zum Abfragen / Abrufen?
Rolando

Es ist keine schlechte Praxis, es impliziert nur eine gewisse Abhängigkeit von den Bibliotheken, die Ihre Hauptbibliothek verwendet. Wenn Sie mit Mongoengine nicht das erreichen können, was Sie brauchen, dann lassen Sie sich auf Pymongo fallen. Es ist das gleiche mit Django MongoDB. Mit dem späteren würden Sie versuchen, innerhalb des Django-ORM zu bleiben, um den agnostischen Zustand des Backends aufrechtzuerhalten. Aber manchmal kann man nicht das tun, was man in der Abstraktion braucht, also lässt man eine Ebene fallen. In diesem Fall hat dies nichts mit Ihrem Problem zu tun, da Sie nur Dienstprogrammmethoden verwenden, um das JSON-Format zu begleiten.
JDI

Ich probiere dies mit Flask aus und es scheint, dass ich mit json.dump keinen jsonify () - Wrapper darum legen kann, so dass er in application / json zurückgegeben wird. Versuch, jsonify zurückzugeben (json.dumps (Beispiel, Standard = json_util.default))
Rolando

2
@amit Es geht nicht so sehr darum, sich die Syntax zu merken, sondern darum, die Dokumentation gut zu lesen und genügend Informationen in meinem Kopf zu speichern, um zu erkennen, wo und wann ich sie erneut abrufen muss. In diesem Fall könnte man "Oh, ein benutzerdefiniertes Objekt mit json" sagen und dann diese Verwendung schnell
aktualisieren

2
@guyskk Ich habe keine Änderungen in bjson oder mongo verfolgt, seit ich dies vor 5 Jahren geschrieben habe. Wenn Sie jedoch die Serialisierung von datetime steuern möchten, müssen Sie Ihre eigene Standardhandlerfunktion schreiben, wie in der Antwort von jgbarah
jdi

619

Mein schneller und schmutziger JSON-Dump, der Datteln und alles frisst:

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

14
Das ist großartig, aber leider habe ich nicht verstanden, was passiert ist? Kann jemand diese Antwort erklären?
Kishor Pawar

63
@KishorPawar: defaultist eine Funktion, die auf Objekte angewendet wird, die nicht serialisierbar sind. In diesem Fall strkonvertiert es einfach alles, was es nicht weiß, in Zeichenfolgen. Das ist großartig für die Serialisierung, aber nicht so großartig beim Deserialisieren (daher das "Quick & Dirty"), da alles ohne Vorwarnung mit einem String versehen werden könnte, z. B. eine Funktion oder ein Numpy-Array.
Mark

1
@ Mark super. Vielen Dank. Nützlich, wenn Sie den Typ dieser nicht serialisierbaren Werte wie Datumsangaben kennen.
Kishor Pawar

2
Warum bin ich mein ganzes Leben lang gegangen, ohne das zu wissen? :)
Arel

1
@jjmontes, funktioniert nicht für alles, zB json.dumps({():1,type(None):2},default=str)erhöht TypeError, kann Typ oder Tupel nicht haben.
Alancalvitti

443

Aufbauend auf anderen Antworten eine einfache Lösung, die auf einem bestimmten Serializer basiert, der nur Zeichenfolgen konvertiert datetime.datetimeund datetime.datein Zeichenfolgen umwandelt .

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

Wie zu sehen ist, prüft der Code nur, ob das Objekt der Klasse datetime.datetimeoder entspricht datetime.date, und erstellt dann .isoformat()eine serialisierte Version davon gemäß dem ISO 8601-Format JJJJ-MM-TTTHH: MM: SS (das mit JavaScript leicht dekodiert werden kann) ). Wenn komplexere serialisierte Darstellungen gesucht werden, kann anstelle von str () ein anderer Code verwendet werden (Beispiele finden Sie in den anderen Antworten auf diese Frage). Der Code endet mit dem Auslösen einer Ausnahme, um den Fall zu behandeln, dass er mit einem nicht serialisierbaren Typ aufgerufen wird.

Diese json_serial-Funktion kann wie folgt verwendet werden:

from datetime import datetime
from json import dumps

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

Einzelheiten zur Funktionsweise des Standardparameters für json.dumps finden Sie im Abschnitt Grundlegende Verwendung der Dokumentation zum json-Modul .


5
Ja, die richtige Antwort, hübscher import datetime und wenn isinstance (obj, datetime.datetime), habe ich viel Zeit verloren, weil ich nicht von datetime import datetime verwendet habe, trotzdem danke
Sérgio

12
aber das erklärt nicht, wie man es mit dem richtigen Typ deserialisiert, nicht wahr?
BlueTrin

2
Nein, @BlueTrin, darüber wurde nichts gesagt. In meinem Fall deserialisiere ich in JavaScript, was sofort funktioniert.
Jgbarah

1
Dies führt zu unerwartetem Verhalten, wenn das JSON-Modul jemals aktualisiert wird, um die Serialisierung von Datetime-Objekten einzuschließen.
Justin

1
@serg Aber die Konvertierungszeiten in UTC würden sich vereinheitlichen 01:00:00+01:00und 02:00:00+00:00sollten je nach Kontext nicht gleich sein. Sie beziehen sich natürlich auf denselben Zeitpunkt, aber der Versatz kann ein relevanter Aspekt des Werts sein.
Alfe

211

Ich bin gerade auf dieses Problem gestoßen und meine Lösung besteht in der Unterklasse 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)

Machen Sie in Ihrem Anruf etwas wie: json.dumps(yourobj, cls=DateTimeEncoder)Das habe .isoformat()ich aus einer der obigen Antworten erhalten.


22
erhöht, weil die Implementierung eines benutzerdefinierten JSONEncoders der richtige Weg sein sollte
3k

25
Dies sollte nicht nur die beste Antwort sein, sondern auch Teil des regulären JSON-Encoders. Wenn nur die Dekodierung weniger mehrdeutig wäre ..
Joost

4
Für diejenigen, die Django verwenden, siehe DjangoJSONEncoder. docs.djangoproject.com/de/dev/topics/serialization/…
S. Kirby

4
Super hilfreich. Letzte Zeile könnte seinreturn super(DateTimeEncoder, self).default(o)
Bob Stein

16
Mit Python 3 ist die letzte Zeile noch einfacher:return super().default(o)
Ariddell

124

Konvertieren Sie das Datum in eine Zeichenfolge

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

10
Und wie könnte ich es in Python deserialisieren?
Wobmene

62
Das Problem besteht darin, dass viele Datetime-Objekte tief in eine Datenstruktur eingebettet sind oder zufällig sind. Dies ist keine zuverlässige Methode.
Rebs

3
zu deserialisieren : oDate = datetime.datetime.strptime(sDate, '%Y-%m-%d %H:%M:%S.%f'). Formate erhalten von: docs.python.org/2/library/datetime.html
Roman

13
Herabgestuft, da Zeitzoneninformationen ignoriert werden. Denken Sie daran, dass .now()die Ortszeit verwendet wird, ohne dies anzugeben. Zumindest .utcnow()sollte verwendet werden (und dann ein +0000 oder Z angehängt)
Daniel F

1
@DanielF At least .utcnow() should be usedNicht genau, datetime.now(timezone.utc)wird empfohlen, siehe Warnung in: docs.python.org/3.8/library/… .
Toreno96

79

Für andere, die die Pymongo-Bibliothek dafür nicht benötigen oder verwenden möchten, können Sie mit diesem kleinen Snippet auf einfache Weise eine JSON-Konvertierung von Datum und Uhrzeit erreichen:

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

Dann benutze es so:

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

Ausgabe: 

'1365091796124'

1
Sollte nicht millis=in der if-Anweisung eingerückt werden? Es ist wahrscheinlich auch besser, str (obj) zu verwenden, um das ISO-Format zu erhalten, das meiner Meinung nach häufiger vorkommt.
Rebs

Warum soll es eingerückt werden? Dieses Snippet funktioniert und die resultierende Ausgabe kann leicht aus Javascript deserialisiert / analysiert werden.
Jay Taylor

5
Da obj möglicherweise kein Objekt [Uhrzeit, Datum, Datum / Uhrzeit] ist
Rebs

2
Ihr Beispiel ist falsch, wenn die lokale Zeitzone einen UTC-Offset ungleich Null hat (die meisten davon). datetime.now()Gibt die Ortszeit zurück (als naives Datum / Uhrzeit-Objekt), aber Ihr Code geht davon aus, dass dies objin UTC ist, wenn es nicht zeitzonenbewusst ist. Verwenden Sie datetime.utcnow()stattdessen.
JFS

1
Es wurde angepasst, um einen Typfehler auszulösen, wenn obj gemäß der Python-Dokumentationsempfehlung unter docs.python.org/2/library/json.html#basic-usage nicht erkannt wird .
Jay Taylor

40

Hier ist meine Lösung:

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

Dann können Sie es so verwenden:

json.dumps(dictionnary, cls=DatetimeEncoder)

zustimmen. Viel besser, zumindest außerhalb des Mongodb-Kontexts. Sie können isinstance(obj, datetime.datetime)innerhalb des TypeError weitere zu behandelnde Typen hinzufügen und mit dem str(obj)oder abschließen repr(obj). Und alle Ihre Dumps können nur auf diese spezialisierte Klasse verweisen.
JL Peyret

@ Natim diese Lösung ist die beste. +1
Souvik Ray

20

Ich habe eine Bewerbung mit einem ähnlichen Problem. Mein Ansatz war es, den Datum / Uhrzeit-Wert als Liste mit 6 Elementen (Jahr, Monat, Tag, Stunde, Minuten, Sekunden) zu JSONisieren. Sie könnten als 7-Punkte-Liste auf Mikrosekunden gehen, aber ich musste nicht:

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)

produziert:

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

Funktioniert nicht, wenn die eingesparte Zeit durch datetime.utcnow ()
saurshaz

1
Welchen Fehler sehen Sie mit datetime.utcnow ()? Es funktioniert gut für mich.
Codingatty

17

Meine Lösung (mit weniger Ausführlichkeit, denke ich):

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)

Dann verwenden Sie jsondumpsanstelle von json.dumps. Es wird gedruckt:

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

Wenn Sie möchten, können Sie später mit einer einfachen Änderung der defaultMethode weitere Sonderfälle hinzufügen . Beispiel:

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
Sie sollten isinstance (o, (datetime.date, datetime.datetime,)) verwenden. Es würde wahrscheinlich nicht schaden, auch datetime.time einzuschließen.
Rebs

Ich denke nicht, dass dies eine gute Lösung mehr ist. Wahrscheinlich sollten die Konvertierungen einen privilegierteren Platz - und auch einen verständlicheren Platz - in Ihrem Code einnehmen, damit Sie wissen, in was Sie konvertieren, wenn Sie Dinge in eine Datenbank oder was auch immer einfügen, anstatt dass alles von a erledigt wird transparente Funktion. Aber ich weiß es nicht.
Fiatjaf

1
JSON eignet sich zum Serialisieren von Daten zur späteren Verarbeitung. Möglicherweise wissen Sie nicht genau, um welche Daten es sich handelt. Und das solltest du nicht brauchen. Das Serialisieren von JSON sollte einfach funktionieren. Genau wie das Konvertieren von Unicode in ASCII sollte. Die Unfähigkeit von Python, dies ohne undurchsichtige Funktionen zu tun, macht die Verwendung ärgerlich. Die Datenbankvalidierung ist ein separates Problem, IMO.
Rebs

Nein, es sollte nicht "nur funktionieren". Wenn Sie nicht wissen, wie die Serialisierung erfolgt ist und später von einem anderen Programm / einer anderen Sprache aus auf die Daten zugreifen müssen, sind Sie verloren.
Fiatjaf

2
JSON wird häufig für Zeichenfolgen, Ints, Floats und Datumsangaben verwendet (ich bin sicher, andere verwenden häufig auch Währung und Temperaturen). Datetime ist jedoch Teil der Standardbibliothek und sollte die De- / Serialisierung unterstützen. Ohne diese Frage würde ich meine unglaublich komplexen json-Blobs (für die ich nicht immer die Struktur erstellt habe) immer noch manuell nach Datumsangaben durchsuchen und sie 1 zu 1 serialisieren.
Rebs

16

Dieses Q wird immer wieder wiederholt - eine einfache Möglichkeit, das JSON-Modul so zu patchen, dass die Serialisierung datetime unterstützt.

import json
import datetime

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

Verwenden Sie dann wie immer die JSON-Serialisierung - diesmal mit datetime als Isoformat.

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

Ergebnis: '{"created": "2015-08-26T14: 21: 31.853855"}'

Weitere Details und einige Hinweise zur Vorsicht finden Sie unter: StackOverflow: JSON-Datumszeit zwischen Python und JavaScript


Affenpflaster FTW. Das Schlimme ist natürlich, dass dies das Verhalten des JSON-Moduls in Ihrer gesamten Anwendung ändert, was andere in einer großen Anwendung überraschen kann und daher imho mit Vorsicht verwendet werden sollte.
Jaap Versteegh

15

Die Methode json.dumps kann einen optionalen Parameter namens default akzeptieren, von dem erwartet wird, dass er eine Funktion ist. Jedes Mal, wenn JSON versucht, einen Wert zu konvertieren, weiß es nicht, wie es konvertiert werden soll, wird die Funktion aufgerufen, die wir an ihn übergeben haben. Die Funktion empfängt das betreffende Objekt und es wird erwartet, dass die JSON-Darstellung des Objekts zurückgegeben wird.

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

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

14

Wenn Sie Python3.7 verwenden, ist die beste Lösung die Verwendung von datetime.isoformat()und datetime.fromisoformat(); Sie arbeiten sowohl mit naiven als auch mit bewussten datetimeObjekten:

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

Ausgabe:

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

Wenn Sie Python3.6 oder niedriger verwenden und sich nur um den Zeitwert (nicht um die Zeitzone) kümmern, können Sie stattdessen datetime.timestamp()und datetime.fromtimestamp()verwenden.

Wenn Sie Python3.6 oder niedriger verwenden und sich für die Zeitzone interessieren, können Sie diese über abrufen datetime.tzinfo, aber Sie müssen dieses Feld selbst serialisieren. Der einfachste Weg, dies zu tun, besteht darin, _tzinfodem serialisierten Objekt ein weiteres Feld hinzuzufügen .

Achten Sie schließlich auf die Präzision in all diesen Beispielen.


datetime.isoformat () ist auch in Python 2.7 vorhanden: docs.python.org/2/library/…
powlo

11

Sie sollten .strftime()method on .datetime.now()method verwenden, um es als serialisierbare Methode zu erstellen .

Hier ist ein Beispiel:

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

Ausgabe:

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

10

Hier ist eine einfache Lösung, um das Problem "datetime not JSON serializable" zu überwinden.

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)

Ausgabe: -> {"Datum": "2015-12-16T04: 48: 20.024609"}


8

Sie müssen eine benutzerdefinierte Encoderklasse mit dem clsParameter von angeben json.dumps. Um aus den Dokumenten zu zitieren :

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

Dies verwendet komplexe Zahlen als Beispiel, aber Sie können genauso einfach eine Klasse zum Codieren von Datumsangaben erstellen (außer ich denke, JSON ist ein wenig verschwommen in Bezug auf Datumsangaben).


5

Der einfachste Weg, dies zu tun, besteht darin, den Teil des Diktats im Datum / Uhrzeit-Format in Isoformat zu ändern. Dieser Wert ist effektiv eine Zeichenfolge in isoformat, mit der json einverstanden ist.

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

5

Eigentlich ist es ganz einfach. Wenn Sie Datumsangaben häufig serialisieren müssen, arbeiten Sie mit ihnen als Zeichenfolgen. Sie können sie bei Bedarf problemlos als Datums- / Uhrzeitobjekte zurückkonvertieren.

Wenn Sie hauptsächlich als datetime-Objekte arbeiten müssen, konvertieren Sie sie vor der Serialisierung als Zeichenfolgen.

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

Wie Sie sehen, ist die Ausgabe in beiden Fällen gleich. Nur der Typ ist unterschiedlich.


3

Wenn Sie das Ergebnis in einer Ansicht verwenden, müssen Sie eine ordnungsgemäße Antwort zurückgeben. Laut API führt jsonify Folgendes aus:

Erstellt eine Antwort mit der JSON-Darstellung der angegebenen Argumente mit einem application / json-Mimetyp.

Um dieses Verhalten mit json.dumps nachzuahmen, müssen Sie einige zusätzliche Codezeilen hinzufügen.

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

Sie sollten auch ein Diktat zurückgeben, um die Antwort von jsonify vollständig zu replizieren. Die gesamte Datei sieht also so aus

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
Frage hat nichts mit Flasche zu tun.
Zoran Pavlovic

2
Die Frage ist über Python. Meine Antwort löst die Frage mit Python. Das OP sagte nicht, ob die Lösung bestimmte Bibliotheken einschließen oder ausschließen sollte. Es ist auch nützlich für alle anderen, die diese Frage lesen und eine Alternative zu suchen pymongo.
Reubano

Bei dieser Frage geht es sowohl um Python als auch nicht um Flask. Der Kolben wird für Ihre Antwort auf die Frage nicht einmal benötigt, daher schlage ich vor, dass Sie ihn entfernen.
Zoran Pavlovic

3

Versuchen Sie dieses mit einem Beispiel, um es zu analysieren:

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

Meine Lösung ...

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, jetzt ein paar Tests.

# 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

Hier ist meine vollständige Lösung für die Konvertierung von datetime in JSON und zurück.

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

Ausgabe

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

JSON-Datei

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

Dadurch konnte ich Zeichenfolgen, Ints, Floats und Datetime-Objekte importieren und exportieren. Es sollte nicht zu schwer sein, für andere Typen zu erweitern.


1
Es explodiert in Python 3 mit TypeError: 'str' does not support the buffer interface. Es liegt am 'wb'offenen Modus, sollte sein 'w'. Es kommt auch zu einer Deserialisierung, wenn wir Daten haben, die dem Datum ähnlich sind, '0000891618-05-000338'aber nicht mit dem Muster übereinstimmen.
Omikron

2

Konvertieren Sie die date in string

date = str(datetime.datetime(somedatetimehere)) 

jjmontes Antwort macht genau das, aber ohne die Notwendigkeit, es explizit für jedes Datum zu tun ...
Bluesummers

2

Im Allgemeinen gibt es verschiedene Möglichkeiten, Datumsangaben zu serialisieren:

  1. ISO-Zeichenfolge, kurz und kann Zeitzoneninformationen enthalten, z. B. die Antwort von @ jgbarah
  2. Zeitstempel (Zeitzonendaten gehen verloren), zB @ JayTaylors Antwort
  3. Wörterbuch der Eigenschaften (einschließlich Zeitzone).

Wenn Sie mit dem letzten Weg einverstanden sind , verarbeitet das Paket json_tricks Datums-, Uhrzeit- und Datumsangaben einschließlich Zeitzonen.

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

was gibt:

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

Alles was Sie tun müssen, ist

`pip install json_tricks`

und dann importieren von json_tricksstatt json.

Der Vorteil, dass es nicht als einzelne Zeichenfolge, int oder float gespeichert wird, ergibt sich beim Decodieren: Wenn Sie nur auf eine Zeichenfolge oder insbesondere auf int oder float stoßen, müssen Sie etwas über die Daten wissen, um zu wissen, ob es sich um eine Datums- / Uhrzeitangabe handelt. Als Diktat können Sie Metadaten speichern, damit sie automatisch dekodiert werden können, was json_tricksfür Sie der Fall ist. Es ist auch leicht für Menschen bearbeitbar.

Haftungsausschluss: Es ist von mir gemacht. Weil ich das gleiche Problem hatte.


1

Ich habe die gleiche Fehlermeldung erhalten, als ich den Serialize Decorator in eine Klasse mit SQLalchemy geschrieben habe. Also statt:

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

Ich habe mir einfach die Idee von jgbarah geliehen, isoformat () zu verwenden, und den ursprünglichen Wert mit isoformat () angehängt, so dass es jetzt so aussieht:

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

1

Eine schnelle Lösung, wenn Sie Ihre eigene Formatierung wünschen

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

Wenn Sie sich auf beiden Seiten der Kommunikation befinden, können Sie die Funktionen repr () und eval () zusammen mit json verwenden.

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

Sie sollten datetime nicht als importieren

from datetime import datetime

da wird sich eval beschweren. Oder Sie können datetime als Parameter an eval übergeben. In jedem Fall sollte dies funktionieren.


0

Ich hatte das gleiche Problem beim Externalisieren des Django-Modellobjekts als JSON festgestellt. Hier ist, wie Sie es lösen können.

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

Verwendung des oben genannten Dienstprogramms:

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

0

Diese Bibliothek Superjson kann es tun. Sie können den JSON-Serializer ganz einfach für Ihr eigenes Python-Objekt anpassen, indem Sie dieser Anweisung folgen: https://superjson.readthedocs.io/index.html#extend .

Das allgemeine Konzept lautet:

Ihr Code muss die richtige Serialisierungs- / Deserialisierungsmethode basierend auf dem Python-Objekt finden. Normalerweise ist der vollständige Klassenname eine gute Kennung.

Und dann sollte Ihre ser / deser-Methode in der Lage sein, Ihr Objekt in ein reguläres serialisierbares Json-Objekt umzuwandeln, eine Kombination aus generischem Python-Typ, Diktat, Liste, Zeichenfolge, Int, Float. Und implementieren Sie Ihre Deser-Methode umgekehrt.


-1

Ich kann nicht 100% richtig sein, aber dies ist der einfache Weg, um zu serialisieren

#!/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'> 
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.