Hoe overwin je “datetime.datetime not JSON serializable”?

Ik heb een basisdictaat als volgt:

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

Als ik jsonify(sample)probeer te doen, krijg ik:

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

Wat kan ik doen zodat mijn woordenboekvoorbeeld de bovenstaande fout kan verhelpen?

Opmerking:hoewel het misschien niet relevant is, worden de woordenboeken gegenereerd door het ophalen van records uit mongodbwaar wanneer ik str(sample['somedate']), de uitvoer is 2012-08-08 21:46:24.862000.


Antwoord 1, autoriteit 100%

Bijgewerkt voor 2018

Het oorspronkelijke antwoord paste bij de manier waarop MongoDB “datum”-velden werden weergegeven als:

{"$date": 1506816000000}

Als je een generieke Python-oplossing wilt voor het serialiseren van datetimenaar json, bekijk dan @jjmontes’ antwoordvoor een snelle oplossing die geen afhankelijkheden vereist.


Omdat je mongoengine gebruikt (per opmerkingen) en pymongo een afhankelijkheid is, heeft pymongo ingebouwde hulpprogramma’s om te helpen met json-serialisatie:
http://api.mongodb.org/python/1.10.1 /api/bson/json_util.html

Voorbeeld van gebruik (serialisatie):

from bson import json_util
import json
json.dumps(anObject, default=json_util.default)

Voorbeeld van gebruik (deserialisatie):

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

Django

Django biedt een native DjangoJSONEncoderserializer die dit soort correct afhandelt.

Zie https://docs.djangoproject.com/en/dev/ topics/serialisatie/#djangojsonencoder

from django.core.serializers.json import DjangoJSONEncoder
return json.dumps(
  item,
  sort_keys=True,
  indent=1,
  cls=DjangoJSONEncoder
)

Een verschil dat ik heb opgemerkt tussen DjangoJSONEncoderen het gebruik van een aangepaste defaultzoals deze:

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
)

Is dat Django een beetje van de gegevens stript:

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

Dus in sommige gevallen moet je daar misschien voorzichtig mee zijn.


Antwoord 2, autoriteit 99%

Mijn snelle & vuile JSON-dump die dadels en alles eet:

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

defaultis een functie die wordt toegepast op objecten die niet serialiseerbaar zijn.
In dit geval is het str, dus het converteert alles wat het niet weet naar strings. Dat is geweldig voor serialisatie, maar niet zo geweldig bij het deserialiseren (vandaar de “quick & dirty”), omdat alles zonder waarschuwing kan zijn gestringificeerd, b.v. een functie of numpy array.


Antwoord 3, autoriteit 99%

Voortbouwend op andere antwoorden, een eenvoudige oplossing gebaseerd op een specifieke serializer die gewoon datetime.datetimeen datetime.dateobjecten converteert naar strings.

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

Zoals te zien is, controleert de code alleen of het object van de klasse datetime.datetimeof datetime.dateis, en gebruikt dan .isoformat()om er een geserialiseerde versie van te maken, volgens ISO 8601-indeling, JJJJ-MM-DDTHH:MM:SS (die gemakkelijk kan worden gedecodeerd door JavaScript). Als er wordt gezocht naar complexere geserialiseerde representaties, kan andere code worden gebruikt in plaats van str() (zie andere antwoorden op deze vraag voor voorbeelden). De code eindigt met het genereren van een uitzondering, om het geval af te handelen dat het wordt aangeroepen met een niet-serialiseerbaar type.

Deze json_serial-functie kan als volgt worden gebruikt:

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

De details over hoe de standaardparameter voor json.dumps werkt, vindt u in Sectie Basisgebruik van de json-moduledocumentatie.


Antwoord 4, autoriteit 50%

Ik ben net dit probleem tegengekomen en mijn oplossing is om json.JSONEncoderte subklassen:

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)

Doe in uw oproep iets als: json.dumps(yourobj, cls=DateTimeEncoder)De .isoformat()die ik heb gekregen van een van de bovenstaande antwoorden.


Antwoord 5, autoriteit 28%

Zet de datum om in een string

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

Antwoord 6, autoriteit 17%

Voor anderen die de pymongo-bibliotheek hiervoor niet nodig hebben of willen gebruiken.. je kunt eenvoudig datetime JSON-conversie bereiken met dit kleine 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,))

Gebruik het dan als volgt:

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

uitvoer: 

'1365091796124'

Antwoord 7, autoriteit 12%

Hier is mijn oplossing:

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

Dan kun je het zo gebruiken:

json.dumps(dictionnary, cls=DatetimeEncoder)

Antwoord 8, autoriteit 6%

als je python3.7 gebruikt, dan is de beste oplossing het gebruik van
datetime.isoformat()en
datetime.fromisoformat(); ze werken met zowel naïeve als
bewust datetimeobjecten:

#!/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() }
    raise TypeError('...')
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))

uitvoer:

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

als u python3.6 of lager gebruikt en u alleen om de tijdswaarde geeft (niet
de tijdzone), dan kun je datetime.timestamp()en . gebruiken
datetime.fromtimestamp()in plaats daarvan;

als je python3.6 of lager gebruikt, en je geeft wel om de tijdzone, dan
je kunt het krijgen via datetime.tzinfo, maar je moet dit veld serialiseren
alleen; de gemakkelijkste manier om dit te doen is door een ander veld _tzinfotoe te voegen in de
geserialiseerd object;

Tot slot, pas op voor precisie in al deze voorbeelden;


Antwoord 9, autoriteit 5%

De json.dumps-methode kan een optionele parameter met de naam default accepteren, waarvan wordt verwacht dat het een functie is. Elke keer dat JSON een waarde probeert te converteren, weet het niet hoe het moet worden geconverteerd, wordt de functie aangeroepen die we eraan hebben doorgegeven. De functie ontvangt het object in kwestie en er wordt verwacht dat het de JSON-representatie van het object retourneert.

def myconverter(o):
 if isinstance(o, datetime.datetime):
    return o.__str__()
print(json.dumps(d, default = myconverter)) 

Antwoord 10, autoriteit 4%

Ik heb een applicatie met een soortgelijk probleem; mijn benadering was om de datetime-waarde te JSONiseren als een lijst met 6 items (jaar, maand, dag, uur, minuten, seconden); je zou naar microseconden kunnen gaan als een lijst van 7 items, maar ik had geen behoefte om:

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)

produceert:

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

Antwoord 11, autoriteit 4%

Mijn oplossing (met minder breedsprakigheid, denk ik):

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)

Gebruik dan jsondumpsin plaats van json.dumps. Het zal afdrukken:

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

Als je wilt, kun je hier later andere speciale gevallen aan toevoegen met een simpele draai aan de default-methode. Voorbeeld:

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)

Antwoord 12, autoriteit 4%

Deze Q wordt keer op keer herhaald – een eenvoudige manier om de json-module te patchen zodat serialisatie datetime zou ondersteunen.

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

Gebruik dan json-serialisatie zoals u altijd doet – dit keer met datetime geserialiseerd als isoformat.

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

resulterend in: ‘{“gemaakt”: “2015-08-26T14: 21: 31.853855”}’

Zie meer details en enkele woorden van waarschuwing op:
Stackoverflow: JSON DATETIME tussen Python en Javascript


13, Autoriteit 2%

U moet een aangepaste encoderklasse leveren met de clsparameter van json.dumps. Om te citeren vanuit de 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', ']']

Hiermee gebruikt u complexe getallen als het voorbeeld, maar u kunt net zo gemakkelijk een klasse maken om data te coderen (behalve dat ik denk dat JSON een beetje fuzzy is over datums)


14, Autoriteit 2%

Hier is een eenvoudige oplossing om te komen “DateDime Not JSON Serializable”
probleem.

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)

OUTPUT: – & GT; {“Datum”: “2015-12-16T04: 48: 20.024609”}


15

De eenvoudigste manier om dit te doen is het deel van het dict te veranderen dat in datetime-formaat is in ISOFORMAT. Die waarde zal effectief een tekenreeks zijn in Isoformat waar JSON OK is.

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

16

Eigenlijk is het vrij eenvoudig.
Als u datums vaak serialiseert, werkt u dan met ze als snaren. Je kunt ze gemakkelijk terugzetten als DateDime-objecten indien nodig.

Als u meestal als dieetime-objecten moet werken, converteer ze dan als snaren voordat u het serialiseert.

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

Zoals u kunt zien, is de uitvoer in beide gevallen hetzelfde. Alleen het type is anders.


17

Probeer deze met een voorbeeld om het te ontleden:

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

Antwoord 18

Als u het resultaat in een weergave gebruikt, moet u ervoor zorgen dat u een correct antwoord geeft. Volgens de API doet jsonify het volgende:

Creëert een reactie met de JSON-representatie van de gegeven argumenten
met een applicatie/json-mimetype.

Om dit gedrag met json.dumps na te bootsen, moet je een paar extra regels code toevoegen.

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

Je moet ook een dictaat retourneren om het antwoord van jsonify volledig te repliceren. Het hele bestand ziet er dus zo uit

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

Antwoord 19

Mijn oplossing …

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, nu wat 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

Antwoord 20

Hier is mijn volledige oplossing voor het converteren van datetime naar JSON en terug..

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

Uitgang

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

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

Hierdoor heeft mij in staat gesteld en exporteert u strings, inten, drijvers en datetime-objecten.
Het zou niet moeilijk moeten zijn om te strekken voor andere typen.


21

Converteer de datenaar string

date = str(datetime.datetime(somedatetimehere)) 

22

Over het algemeen zijn er verschillende manieren om datetimes te serialiseren, zoals:

  1. ISO-string, kort en kan timezone-info bevatten, b.v. @ Jgbarah’s antwoord
  2. Tijdstempel (tijdzone-gegevens zijn verloren), b.v. @ Jaytaylor’s Antwoord
  3. Woordenboek van eigenschappen (inclusief timezone).

Als je akkoord gaat met de laatste manier, verwerkt het json_tricks-pakket datums, tijden en datetimes inclusief tijdzones.

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

wat geeft:

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

Dus alles wat je hoeft te doen is

`pip install json_tricks`

en importeer vervolgens uit json_tricksin plaats van json.

Het voordeel van het niet opslaan als een enkele string, int of float komt bij het decoderen: als je alleen een string of vooral int of float tegenkomt, moet je iets over de gegevens weten om te weten of het een datetime is. Als dictaat kun je metadata opslaan zodat deze automatisch kan worden gedecodeerd, wat json_tricksvoor je doet. Het is ook gemakkelijk te bewerken voor mensen.

Disclaimer: het is door mij gemaakt. Omdat ik hetzelfde probleem had.


Antwoord 23

Volgens het antwoord van @jjmontes heb ik de volgende aanpak gebruikt.
Voor kolf- en kolfrustgevende gebruikers

# get json string
jsonStr = json.dumps(my_dictionary, indent=1, sort_keys=True, default=str)
# then covert json string to json object
return json.loads(jsonStr)

Antwoord 24

Ik heb hetzelfde foutmelding tijdens het schrijven van de serialize decorator in een klas met Sqlalchemy. Dus in plaats van:

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

Ik heb gewoon het idee van Jgbarah geleend van het gebruik van isoformat () en de oorspronkelijke waarde bijgehouden met isoformat (), zodat het er nu uitziet:

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

25

Een snelle oplossing als u uw eigen formattering wilt

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)

26

Als u aan beide zijden van de communicatie bevindt, kunt u REP () en EVAL () -functies samen met JSON gebruiken.

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

U moet DATETIME niet importeren als

from datetime import datetime

Aangezien EVAL zal klagen. Of u kunt DATETIME doorgeven als een parameter voor EVAL. In ieder geval zou dit moeten werken.


27

Ik had hetzelfde probleem tegengekomen bij het externaliseren van Django-modelobject om te dumpen als JSON.
Hier is hoe je het kunt oplossen.

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

28

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

Gebruik van bovenstaande hulpprogramma:

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

29

deze bibliotheek superjson kan het doen. En je kunt eenvoudig JSON Serializer op maat maken voor je eigen Python-object door deze instructie te volgen https: // superjson. leeshedocs.io/index.html#extend .

Het algemene concept is:

Uw code moet de juiste serialisatie / deserialisatiemethode vinden op basis van het Python-object. Meestal is de volledige Classname een goede identifier.

En dan moet uw SER / Deser-methode uw object kunnen transformeren naar een gewone JSON-serialiseerbare object, een combinatie van generiek Python-type, DICT, lijst, string, int, float. En implementeer uw Deser-methode omkeerig.

Other episodes