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.

Rolando
quelle
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
mögliches Duplikat von JSON datetime zwischen Python und JavaScript
jdi
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.

jdi
quelle
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)
jjmontes
quelle
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 .

jgbarah
quelle
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.

Lenny
quelle
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() )
DA
quelle
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'
Jay Taylor
quelle
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)
Natim
quelle
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"}
Codierung
quelle
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)
Fiatjaf
quelle
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

Davidhadas
quelle
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)) 
Saurabh Saha
quelle
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.

Cyker
quelle
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'}
Benyamin Jafari
quelle
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"}

ob92
quelle
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).

Sean Redmond
quelle
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()
Peter Graham
quelle
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.

AngelDown
quelle
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
reubano
quelle
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()
Zhigang
quelle
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
macm
quelle
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.

Hovo
quelle
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)) 
Rana Nematollahi
quelle
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.

Kennzeichen
quelle
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(),
                  ...
Baumfisch Zhang
quelle
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)
Arash
quelle
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.

ThunderBear
quelle
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
naren
quelle
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
Vinod Kumar
quelle
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.

MacSanhe
quelle
-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'> 
Shiva
quelle