Comment surmonter "datetime.datetime not JSON serialisable"?

j'ai un dict de base comme suit:

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

Quand j'ai essayer de faire jsonify(sample) , j'obtiens:

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

Que puis-je faire pour que mon exemple de dictionnaire puisse surmonter l'erreur ci-dessus?

Note: Bien que cela ne soit pas pertinent, les dictionnaires sont générés à partir de la récupération des notices de mongodb où lorsque j'imprime str(sample['somedate']) , la sortie est 2012-08-08 21:46:24.862000 .

444
demandé sur martineau 2012-08-09 06:02:51

27 réponses

mis à jour pour 2018

l'original de La réplique logés à la manière MongoDB "date" champs étaient représentés comme suit:

{"$date": 1506816000000}

si vous voulez une solution générique Python pour sérialiser datetime vers json, cochez réponse de @jjmontes pour une solution rapide qui ne nécessite aucune dépendance.


comme vous utilisez mongoengine (per commentaires) et pymongo est une dépendance, pymongo a des utilitaires intégrés pour aider avec la sérialisation json:

http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

exemple d'utilisation (sérialisation):

from bson import json_util
import json

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

exemple d'utilisation (deserialisation):

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

Django

Django fournit un natif DjangoJSONEncoder serializer qui traite de ce genre de correctement.

voir https://docs.djangoproject.com/en/dev/topics/serialization/#djangojsonencoder

from django.core.serializers.json import DjangoJSONEncoder

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

une différence que j'ai remarqué entre DjangoJSONEncoder et l'utilisation d'une coutume default comme ceci:

def default(o):
  if type(o) is datetime.date or type(o) is datetime.datetime:
    return o.isoformat()

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

est que Django dépouille un peu des données:

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

donc, vous pourriez avoir besoin d'être prudent à ce sujet dans certains cas.

227
répondu jdi 2018-08-04 21:43:12

S'appuyant sur d'autres réponses, une solution simple basée sur un sérialiseur spécifique qui convertit simplement les objets datetime.datetime et datetime.date en cordes.

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

comme vu, le code vérifie simplement si l'objet est de la classe datetime.datetime ou datetime.date , puis utilise .isoformat() pour produire une version sérialisée de celui-ci, selon le format ISO 8601, AAAA-MM-JJTHH:MM:SS (qui est facilement décodé par JavaScript). Si plus complexe sérialisé des représentations sont recherchées, un autre code pourrait être utilisé au lieu de str () (voir les autres réponses à cette question pour des exemples). Le code se termine en soulevant une exception, pour traiter le cas où il est appelé avec un type non-serialisable.

cette fonction json_serial peut être utilisée comme suit:

from datetime import datetime
from json import dumps

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

les détails sur la façon dont le paramètre par défaut à json.les travaux de dumps peuvent être trouvés dans la section utilisation de base de la documentation du module json .

346
répondu jgbarah 2018-03-09 00:49:23

Mon quick & dirty JSON dump qui mange les dates et tout et tout:

json.dumps(my_dictionary, indent=4, sort_keys=True, default=str)
253
répondu jjmontes 2017-01-19 21:37:07

je viens de rencontrer ce problème et ma solution est la sous-classe 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)

dans votre appel faire quelque chose comme: json.dumps(yourobj, cls=DateTimeEncoder) le .isoformat() j'ai obtenu de l'une des réponses ci-dessus.

151
répondu lenny 2014-11-21 09:52:44

convertissez la date en chaîne de caractères

sample['somedate'] = str( datetime.utcnow() )
110
répondu D.A 2018-04-08 13:56:58

pour ceux qui n'ont pas besoin ou ne veulent pas utiliser la bibliothèque pymongo pour cela.. vous pouvez facilement réaliser la conversion datetime JSON avec ce petit morceau:

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

alors utilisez - le comme ceci:

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

sortie:

'1365091796124'
76
répondu Jay Taylor 2016-01-05 13:34:07

voici ma solution:

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

, Alors vous pouvez l'utiliser comme ça:

json.dumps(dictionnary, cls=DatetimeEncoder)
28
répondu Natim 2016-08-24 07:57:22

j'ai une application avec un problème similaire; mon approche était de Jsoniser la valeur de datetime comme une liste de 6 articles (Année, Mois, Jour, Heure, minutes, secondes); vous pourriez aller aux microsecondes comme une liste de 7 articles, mais je n'avais pas besoin de:

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)

produit:

{'somedate': datetime.datetime(2013, 8, 1, 16, 22, 45, 890000), 'title': 'String'}
{"somedate": [2013, 8, 1, 16, 22, 45], "title": "String"}
20
répondu codingatty 2013-08-01 23:27:38

Ma solution (à moins de verbosité, je crois):

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)

alors utilisez jsondumps au lieu de json.dumps . Il affichera:

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

I vous voulez, plus tard vous pouvez ajouter d'autres cas spéciaux à cela avec une simple torsion de la méthode default . Exemple:

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)
15
répondu fiatjaf 2013-07-31 00:07:54

ce Q répète encore et encore - une façon simple de patcher le module json de sorte que la sérialisation prendrait en charge datetime.

import json
import datetime

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

que d'utiliser la sérialisation json comme vous le faites toujours - Cette fois avec datetime étant sérialisé comme isoformat.

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

: '{"créé": "2015-08-26T14:21:31.853855"}'

Voir plus de détails et quelques mots de prudence à: StackOverflow: JSON datetime entre Python et JavaScript

15
répondu davidhadas 2017-05-23 12:26:33

Voici une solution simple à venir " datetime Non JSON serialisable" problème.

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)

sortie: - > {"date": "2015-12-16T04: 48:20.024609"}

9
répondu ob92 2015-12-16 03:53:51

vous devez fournir une classe d'encodeur personnalisée avec le paramètre cls de json.dumps . Pour citer le 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', ']']

cela utilise des nombres complexes comme exemple, mais vous pouvez tout aussi facilement créer une classe pour encoder des dates (sauf que je pense que JSON est un peu flou sur les dates)

7
répondu Sean Redmond 2012-08-09 03:08:10

la façon la plus simple de faire cela est de changer la partie du dict qui est en format datetime en isoformat. Cette valeur sera effectivement une chaîne dans isoformat avec laquelle json est ok.

v_dict = version.dict()
v_dict['created_at'] = v_dict['created_at'].isoformat()
5
répondu Peter Graham 2015-06-30 18:25:15

vous devez utiliser .strftime() méthode sur .datetime.now() méthode pour sérialisable. comme le code ci-dessous:

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

sortie:

Out[0]: {'a': 1, 'b': 2, 'time': '2017-10-31T15:16:30'}
5
répondu Benyamin Jafari 2018-07-03 19:55:18

Si vous utilisez le résultat en vue d'être sûr de retourner une réponse appropriée. Selon L'API, jsonify fait ce qui suit:

crée une réponse avec la représentation JSON des arguments donnés avec une application/json type mime.

Pour imiter ce comportement avec json.dumps vous devez ajouter quelques lignes de code supplémentaires.

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

, Vous devez également retourner un dict pleinement répliquer la réponse de jsonify. Ainsi, le fichier entier ressemblera à ceci

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
3
répondu reubano 2013-06-09 13:42:57

voici ma solution complète pour convertir datetime en JSON et retour..

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

sortie

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

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

cela m'a permis d'importer et d'exporter des chaînes, des ints, des flotteurs et des objets datetime. Il ne devrait pas être trop difficile à étendre pour d'autres types.

3
répondu Hovo 2016-01-12 04:55:56

convertissez le date en string

date = str(datetime.datetime(somedatetimehere)) 
2
répondu Rana Nematollahi 2016-02-01 06:10:16

il y a généralement plusieurs façons de sérialiser les datetimes, comme:

  1. chaîne ISO, courte et pouvant inclure des informations sur le fuseau horaire, p.ex. de @jgbarah "réponse
  2. Timestamp (les données du fuseau horaire sont perdues), par exemple de @JayTaylor réponse 1519120920"
  3. Dictionnaire de propriétés (y compris fuseau horaire).

si vous êtes d'accord avec la dernière voie, le json_tricks package gère les dates, les heures et datetimes y compris les fuseaux horaires.

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

qui donne:

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

donc tout ce que vous devez faire est

`pip install json_tricks`

et ensuite importer de json_tricks au lieu de json .

l'avantage de ne pas le stocker comme une seule chaîne, int ou float vient lors du décodage: si vous rencontrez juste une chaîne ou particulièrement int ou float, vous devez savoir quelque chose sur les données pour savoir si c'est une datetime. En tant que dict, vous pouvez stocker des métadonnées de sorte qu'elles puissent être décodées automatiquement, ce qui est ce que json_tricks fait pour vous. C'est aussi facilement modifiable pour les humains.

Avertissement: il est fait par moi. Parce que j'ai eu le même problème.

2
répondu Mark 2017-05-23 12:02:58

essayez celui-ci avec un exemple pour l'analyser:

#!/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
répondu zhigang 2018-07-22 06:27:33

si vous êtes des deux côtés de la communication, vous pouvez utiliser les fonctions repr () et eval () avec json.

import datetime, json

dt = datetime.datetime.now()
print("This is now: {}".format(dt))

dt1 = json.dumps(repr(dt))
print("This is serialised: {}".format(dt1))

dt2 = json.loads(dt1)
print("This is loaded back from json: {}".format(dt2))

dt3 = eval(dt2)
print("This is the same object as we started: {}".format(dt3))

print("Check if they are equal: {}".format(dt == dt3))

vous ne devriez pas importer datetime comme

from datetime import datetime

puisque eval se plaindra. Ou vous pouvez passer datetime comme paramètre à eval. En tout cas, cela devrait fonctionner.

2
répondu ThunderBear 2018-08-08 12:48:23

ma solution ...

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, maintenant quelques 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
1
répondu macm 2015-10-22 13:29:54

j'ai reçu le même message d'erreur en écrivant le décorateur serialize à l'intérieur d'une classe avec sqlalchemy. Donc au lieu de:

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

j'ai simplement emprunté l'idée de jgbarah d'utiliser isoformat () et j'ai ajouté la valeur originale avec isoformat (), de sorte qu'elle ressemble maintenant à:

                  ...
                 'date_birth':self.date_birth.isoformat(),
                  ...
1
répondu Treefish Zhang 2015-12-02 22:13:38

une solution rapide si vous voulez votre propre formatage

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
répondu Arash 2016-05-24 13:19:00

Le json.décharges méthode peut accepter un paramètre optionnel appelé par défaut qui devrait être une fonction. Chaque fois que JSON essaie de convertir une valeur, il ne sait pas comment la Convertir il appellera la fonction que nous lui avons passé. La fonction recevra l'objet en question, et son retour est prévu la représentation JSON de l'objet.

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

print(json.dumps(d, default = myconverter)) 
1
répondu Saurabh Saha 2018-07-31 21:23:27

j'avais rencontré le même problème en externalisant l'objet modèle django à dump comme JSON. Voici comment vous pouvez le résoudre.

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
répondu naren 2013-07-20 17:21:41
def j_serial(o):     # self contained
    from datetime import datetime, date
    return str(o).split('.')[0] if isinstance(o, (datetime, date)) else None

au-dessus de l'Utilisation de l'utilitaire:

import datetime
serial_d = j_serial(datetime.datetime.now())
if serial_d:
    print(serial_d)  # output: 2018-02-28 02:23:15
0
répondu Vinod Kumar 2018-02-27 20:54:55

Je ne peux pas corriger à 100% mais, c'est la façon simple de faire serialize

#!/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'> 
-1
répondu Shiva 2017-04-15 08:27:51