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
.
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.
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 .
Mon quick & dirty JSON dump qui mange les dates et tout et tout:
json.dumps(my_dictionary, indent=4, sort_keys=True, default=str)
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.
convertissez la date en chaîne de caractères
sample['somedate'] = str( datetime.utcnow() )
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'
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)
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"}
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)
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
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"}
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)
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()
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'}
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
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.
convertissez le date
en string
date = str(datetime.datetime(somedatetimehere))
il y a généralement plusieurs façons de sérialiser les datetimes, comme:
- chaîne ISO, courte et pouvant inclure des informations sur le fuseau horaire, p.ex. de @jgbarah "réponse
- Timestamp (les données du fuseau horaire sont perdues), par exemple de @JayTaylor réponse 1519120920"
- 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.
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()
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.
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
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(),
...
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)
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))
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
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
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'>