[python] Wie kann "datetime.datetime nicht JSON serializable" überwunden werden?



Answers

Aufbauend auf anderen Antworten, eine einfache Lösung basierend auf einem spezifischen Serializer, der nur datetime.datetime und datetime.date Objekte in Strings konvertiert.

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.datetime oder datetime.date , und verwendet dann .isoformat() , um eine serialisierte Version davon gemäß ISO 8601-Format YYYY-MM-DDTHH zu erzeugen: MM: SS (was einfach durch JavaScript entschlüsselt werden kann). Wenn komplexere serialisierte Darstellungen gesucht werden, könnte anstelle von str () anderer Code verwendet werden (siehe andere Antworten auf diese Frage für Beispiele). Der Code endet mit dem Auslösen einer Ausnahme, um den Fall zu behandeln, in dem 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)

Die Details darüber, wie der Standardparameter für json.dumps funktioniert, finden Sie im Abschnitt Grundlegende Verwendung der Dokumentation des json-Moduls .

Question

Ich habe ein grundlegendes Diktat wie folgt:

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

Wenn ich versuche, jsonify(sample) zu machen jsonify(sample) bekomme ich:

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

Was kann ich tun, damit meine Wörterbuchprobe den obigen Fehler beheben kann?

Hinweis: Obwohl die Wörterbücher möglicherweise nicht relevant sind, werden sie aus dem Abruf von Datensätzen aus mongodb generiert. Wenn ich str(sample['somedate']) lautet die Ausgabe 2012-08-08 21:46:24.862000 .




Sie müssen eine benutzerdefinierte json.dumps mit dem cls Parameter von json.dumps . Um aus den docs 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 erstellen, um Daten zu kodieren (außer ich denke, JSON ist ein wenig unscharf über Daten)




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)

Verwenden jsondumps dann jsondumps anstelle von json.dumps . Es wird gedruckt:

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

Ich will, später können Sie andere Sonderfälle mit einer einfachen Wendung der default 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)



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 Strings, Ints, Floats und Datetime-Objekte importieren und exportieren. Es sollte nicht schwer sein für andere Typen zu erweitern.




https://github.com/django/django/blob/master/django/core/serializers/json.py

Eine portierte Version, die nicht von Django abhängig ist:

import datetime
import decimal
import json
import uuid


class JSONEncoder(json.JSONEncoder):
    """JSONEncoder subclass that knows how to encode date/time, decimal types, and UUIDs."""

    def default(self, o):
        # See "Date Time String Format" in the ECMA-262 specification.
        if isinstance(o, datetime.datetime):
            r = o.isoformat()
            if o.microsecond:
                r = r[:23] + r[26:]
            if r.endswith('+00:00'):
                r = r[:-6] + 'Z'
            return r
        elif isinstance(o, datetime.date):
            return o.isoformat()
        elif isinstance(o, datetime.time):
            if o.utcoffset() is not None:
                raise ValueError("JSON can't represent timezone-aware times.")
            r = o.isoformat()
            if o.microsecond:
                r = r[:12]
            return r
        elif isinstance(o, (decimal.Decimal, uuid.UUID)):
            return str(o)
        else:
            return super(JSONEncoder, self).default(o)

print(json.dumps(datetime.datetime.now(), cls=JSONEncoder))



Für andere, die die pymongo-Bibliothek nicht benötigen oder verwenden möchten, können Sie mit diesem kleinen Ausschnitt eine datetime-JSON-Konvertierung 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'



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)



Ich habe eine Bewerbung mit einem ähnlichen Problem; Mein Ansatz bestand darin, den Datetime-Wert als 6-Item-Liste (Jahr, Monat, Tag, Stunde, Minuten, Sekunden) zu vereinheitlichen. Sie könnten in Mikrosekunden als 7-Item-Liste 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"}



Ich habe die gleiche Fehlermeldung beim Schreiben der Serialize Decorator in einer Klasse mit sqlalchemy. Also statt:

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

Ich lieh mir einfach jgbarahs Idee aus, isoformat () zu verwenden und fügte den ursprünglichen Wert mit isoformat () hinzu, so dass es nun wie folgt aussieht:

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



Ich bin gerade auf dieses Problem gestoßen und meine Lösung ist die 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)

In Ihrem Anruf tun Sie etwas wie: json.dumps(yourobj, cls=DateTimeEncoder) Das .isoformat() ich von einer der Antworten oben bekommen.




Sie sollten die .strftime () -Methode für die .datetime.now () -Methode für serializable verwenden. wie unter dem Code:

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



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 obigen Dienstprogramms:

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





Related



Tags

python python   json json