Как преодолеть " datetime.datetime не сериализуется JSON"?
у меня есть основной дикт следующим образом:
sample = {}
sample['title'] = "String"
sample['somedate'] = somedatetimehere
когда я пытаюсь сделать jsonify(sample) Я:
TypeError: datetime.datetime(2012, 8, 8, 21, 46, 24, 862000) is not JSON serializable
что я могу сделать, чтобы мой образец словаря мог преодолеть ошибку выше?
Примечание: хотя это может быть не актуально, словари генерируются из извлечения записей из mongodb, где, когда я распечатываю str(sample['somedate']) выход 2012-08-08 21:46:24.862000.
27 ответов:
обновлено на 2018 год
исходный ответ соответствовал тому, как поля MongoDB "дата" были представлены как:
{"$date": 1506816000000}если вы хотите общее решение Python для сериализации
datetimeв json, проверьте @jjmontes' ответ для быстрого решения, которое не требует никаких зависимостей.
как вы используете mongoengine (за комментарии) и pymongo является зависимостью, pymongo имеет встроенные утилиты, чтобы помочь с json serialization:
http://api.mongodb.org/python/1.10.1/api/bson/json_util.htmlпример использования (сериализации):
from bson import json_util import json json.dumps(anObject, default=json_util.default)пример использования (десериализации):
json.loads(aJsonString, object_hook=json_util.object_hook)
Джанго
Django предоставляет родной
DjangoJSONEncoderсериализатор, который имеет дело с такого рода должным образом.посмотреть 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 )одно различие я заметил между
DjangoJSONEncoderи с помощью настраиваемогоdefaultтакой: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 )это Джанго полосы немного данных:
"last_login": "2018-08-03T10:51:42.990", # DjangoJSONEncoder "last_login": "2018-08-03T10:51:42.990239", # defaultтак что, возможно, вам придется быть осторожным об этом в некоторых случаях.
основываясь на других ответах, простое решение на основе конкретного сериализатора, который просто преобразует
datetime.datetimeиdatetime.dateобъекты в строки.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))как видно, код просто проверяет, является ли объект классом
datetime.datetimeилиdatetime.date, а затем использует.isoformat()чтобы создать его сериализованную версию, в соответствии с форматом ISO 8601, YYYY-MM-DDTHH:MM: SS (который легко декодируется JavaScript). Если требуются более сложные сериализованные представления, можно использовать другой код вместо str () (см. другие ответы на этот вопрос для примеров). Код заканчивается созданием исключения, чтобы иметь дело с случаем, который он вызывает с несериализуемым типом.эту функцию json_serial можно использовать следующим образом:
from datetime import datetime from json import dumps print dumps(datetime.now(), default=json_serial)подробности о том, как параметр по умолчанию для json.дампы работы можно найти в раздел основное использование документации модуля json.
мой быстрый и грязный JSON дамп, который ест даты и все:
json.dumps(my_dictionary, indent=4, sort_keys=True, default=str)
Я только что столкнулся с этой проблемой, и мое решение-подкласс
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)в вашем вызове сделать что-то вроде:
json.dumps(yourobj, cls=DateTimeEncoder)The.isoformat()я получил от одного из вышеуказанных ответов.
для тех, кто не нуждается или не хочет использовать библиотеку pymongo для этого.. вы можете легко достичь преобразования datetime JSON с помощью этого небольшого фрагмента:
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,))тогда используйте его так:
import datetime, json print json.dumps(datetime.datetime.now(), default=default)выход:
'1365091796124'
вот мое решение:
# -*- 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)тогда вы можете использовать его так:
json.dumps(dictionnary, cls=DatetimeEncoder)
у меня есть приложение с аналогичной проблемой; мой подход состоял в том, чтобы JSONize значение datetime как список из 6 элементов (год, месяц, день, час, минуты, секунды); вы можете перейти к микросекундам как список из 7 элементов, но мне не нужно было:
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)выдает:
{'somedate': datetime.datetime(2013, 8, 1, 16, 22, 45, 890000), 'title': 'String'} {"somedate": [2013, 8, 1, 16, 22, 45], "title": "String"}
мое решение (с меньшим количеством, я думаю):
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)затем использовать
jsondumpsвместоjson.dumps. Он будет печатать:>>> jsondumps({'today': datetime.date.today()}) '{"today": "2013-07-30"}'Я вы хотите, позже вы можете добавить другие специальные случаи к этому с простым поворотом
defaultметод. Пример: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)
этот Q повторяется снова и снова - простой способ исправить модуль json таким образом, что сериализация будет поддерживать datetime.
import json import datetime json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)чем использовать сериализацию json, как вы всегда делаете - на этот раз с сериализацией datetime как изоформат.
json.dumps({'created':datetime.datetime.now()})в результате: '{"создано": "2015-08-26T14: 21: 31.853855"}'
смотрите дополнительную информацию и некоторые слова предостережения на: StackOverflow: JSON datetime между Python и JavaScript
вот простое решение для того, чтобы прийти " datetime not JSON serializable" проблема.
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)выход:-> {"дата": "2015-12-16T04:48:20.024609"}
самый простой способ сделать это-изменить часть dict, которая находится в формате datetime, на isoformat. Это значение будет фактически строкой в isoformat, с которой json в порядке.
v_dict = version.dict() v_dict['created_at'] = v_dict['created_at'].isoformat()
вы должны использовать
.strftime()метод on.datetime.now()метод для сериализации. например, ниже код: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выход:
Out[0]: {'a': 1, 'b': 2, 'time': '2017-10-31T15:16:30'}
Если вы используете результат в представлении, не забудьте вернуть правильный ответ. Согласно API, jsonify делает следующее:
создает ответ с представлением JSON заданных аргументов с помощью приложения / JSON mimetype.
чтобы имитировать это поведение с JSON.отвалите вы должны добавить несколько дополнительных строк кода.
response = make_response(dumps(sample, cls=CustomEncoder)) response.headers['Content-Type'] = 'application/json' response.headers['mimetype'] = 'application/json' return responseвы также должны вернуть dict для полной репликации ответа jsonify. Итак, весь файл будет выглядеть так
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
вот мое полное решение для преобразования datetime в JSON и обратно..
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выход
{'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}Это позволило мне импортировать и экспортировать строки, ints, поплавки и объекты datetime. Это не должно быть трудно расширить для других типов.
как правило, существует несколько способов сериализации даты и времени, например:
- строка ISO, короткая и может включать информацию о часовом поясе, например @jgbarah's ответ
- метка времени (данные часового пояса теряются), например @Jaytaylor's ответ
- словарь свойств (включая часовой пояс).
если вы в порядке с последним способом, то json_tricks пакет обрабатывает даты, время и даты в том числе часовой пояс.
from datetime import datetime from json_tricks import dumps foo = {'title': 'String', 'datetime': datetime(2012, 8, 8, 21, 46, 24, 862000)} dumps(foo)что дает:
{"title": "String", "datetime": {"__datetime__": null, "year": 2012, "month": 8, "day": 8, "hour": 21, "minute": 46, "second": 24, "microsecond": 862000}}так что все, что вам нужно сделать, это
`pip install json_tricks`а затем импортировать из
json_tricksвместоjson.преимущество не хранить его в виде одной строки, int или float приходит при декодировании: если вы сталкиваетесь только строку или особенно int или float, вам нужно знать что-то о данных, чтобы знать, если это datetime. Как dict, вы можете хранить метаданные, чтобы их можно было декодировать автоматически, что и происходит
json_tricksделает для вас. Он также легко редактируется для людей.отказ от ответственности: это сделано мной. Потому что у меня была такая же проблема.
попробуйте это с примером, чтобы разобрать его:
#!/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()
Если вы находитесь на обеих сторонах связи вы можете использовать repr () и eval () функции вместе с 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))вы не должны импортировать datetime как
from datetime import datetimeпоскольку eval будет жаловаться. Или вы можете передать datetime в качестве параметра функции eval. В любом случае это должно сработать.
мое решение ...
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хорошо, теперь некоторые тесты.
# 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
Я получил то же сообщение об ошибке при написании serialize декоратор внутри класса с sqlalchemy. Так что вместо:
Class Puppy(Base): ... @property def serialize(self): return { 'id':self.id, 'date_birth':self.date_birth, ... }Я просто заимствовал идею jgbarah об использовании isoformat () и добавил исходное значение с помощью isoformat (), так что теперь он выглядит так:
... 'date_birth':self.date_birth.isoformat(), ...
быстрое исправление, если вы хотите, чтобы ваше собственное форматирование
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)
json.метод dumps может принимать необязательный параметр с именем default, который, как ожидается, будет функцией. Каждый раз, когда JSON пытается преобразовать значение, он не знает, как преобразовать его, он вызовет функцию, которую мы передали ему. Функция получит рассматриваемый объект, и ожидается, что она вернет представление JSON объекта.
def myconverter(o): if isinstance(o, datetime.datetime): return o.__str__() print(json.dumps(d, default = myconverter))
я столкнулся с той же проблемой при экстернализации объекта модели django для дампа, что и JSON. Вот как вы можете решить.
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использование вышеуказанной утилиты:
import datetime serial_d = j_serial(datetime.datetime.now()) if serial_d: print(serial_d) # output: 2018-02-28 02:23:15
Я не могу 100% правильно, но, это простой способ сделать сериализацию
#!/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'>
Comments