How to overcome “datetime.datetime not JSON serializable”?
我的基本口述如下:
1 2 3 | sample = {} sample['title'] ="String" sample['somedate'] = somedatetimehere |
当我试着做
江户十一〔一〕号
我该怎么做才能让我的字典样本克服上面的错误呢?
注:虽然可能不相关,但字典是从MongoDB的记录中检索出来的,当我打印出
在其他答案的基础上,一个基于特定序列化程序的简单解决方案,它只将
1 2 3 4 5 6 7 8 | 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)) |
号
如图所示,代码只是检查对象是否属于
这个json_串行功能可以如下使用:
1 2 3 4 | from datetime import datetime from json import dumps print dumps(datetime.now(), default=json_serial) |
有关json.dumps的默认参数如何工作的详细信息,可以在json模块文档的基本用法一节中找到。
我的快速和肮脏的JSON转储,吃日期和一切:
1 | json.dumps(my_dictionary, indent=4, sort_keys=True, default=str) |
2018年更新
原始答案符合MongoDB"日期"字段的表示方式:
如果您想要一个通用的python解决方案将
由于您使用的是MongoEngine(每个注释),并且pymongo是一个依赖项,所以pymongo有内置的实用程序来帮助JSON序列化:http://api.mongodb.org/python/1.10.1/api/bson/json_util.html
示例用法(序列化):
1 2 3 4 | from bson import json_util import json json.dumps(anObject, default=json_util.default) |
示例用法(反序列化):
1 | json.loads(aJsonString, object_hook=json_util.object_hook) |
号丹戈
Django提供了一个本地的
参见https://docs.djangoproject.com/en/dev/topics/serialization/djangojsonencoder
1 2 3 4 5 6 7 8 | from django.core.serializers.json import DjangoJSONEncoder return json.dumps( item, sort_keys=True, indent=1, cls=DjangoJSONEncoder ) |
我注意到,
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 ) |
。
Django是否删除了一点数据:
1 2 | "last_login":"2018-08-03T10:51:42.990", # DjangoJSONEncoder "last_login":"2018-08-03T10:51:42.990239", # default |
所以,在某些情况下,你可能需要小心。
我刚刚遇到这个问题,我的解决方案是将
1 2 3 4 5 6 7 8 9 | 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) |
在你的电话里,做一些类似的事情:我从上面的一个答案中得到的
将日期转换为字符串
1 | sample['somedate'] = str( datetime.utcnow() ) |
。
对于其他不需要或不想使用pymongo库的人。您可以使用以下小片段轻松实现日期时间JSON转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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,)) |
然后像这样使用:
1 2 | import datetime, json print json.dumps(datetime.datetime.now(), default=default) |
。
输出:
1 | '1365091796124' |
这是我的解决方案:
1 2 3 4 5 6 7 8 9 10 | # -*- 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) |
。
然后你可以这样使用它:
1 | json.dumps(dictionnary, cls=DatetimeEncoder) |
号
我有一个应用程序也有类似的问题;我的方法是将日期时间值JSonize为6项列表(年、月、日、小时、分钟、秒);您可以将微秒作为7项列表,但我不需要:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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) |
号
生产:
1 2 | {'somedate': datetime.datetime(2013, 8, 1, 16, 22, 45, 890000), 'title': 'String'} {"somedate": [2013, 8, 1, 16, 22, 45],"title":"String"} |
号
这个q一次又一次地重复-一种简单的方法来修补JSON模块,这样序列化就可以支持日期时间。
1 2 3 4 | import json import datetime json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None) |
号
而不是像往常一样使用JSON序列化-这次将datetime序列化为isoformat。
1 | json.dumps({'created':datetime.datetime.now()}) |
产生:""created":"2015-08-26t14:21:31.853855""
更多详情和注意事项请参见:stackoverflow:python和javascript之间的json datetime
我的解决方案(我想用更少的冗长内容):
1 2 3 4 5 6 | 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) |
号
然后用
1 2 | >>> jsondumps({'today': datetime.date.today()}) '{"today":"2013-07-30"}' |
号
我想,稍后您可以通过简单的
1 2 3 4 5 | 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) |
号
下面是一个简单的解决方案,可以覆盖"日期时间而不是JSON可序列化"问题。
1 2 3 4 5 6 7 8 | 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"
您必须为自定义编码器类提供
1 2 3 4 5 6 7 8 9 10 11 12 13 | >>> 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', ']'] |
。
这使用复数作为例子,但是您可以轻松地创建一个类来编码日期(我认为JSON对日期有点模糊)。
您应该在
下面是一个例子:
1 2 3 4 5 6 | 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 |
号
输出:
1 | Out[0]: {'a': 1, 'b': 2, 'time': '2017-10-31T15:16:30'} |
号
dumps方法可以接受一个名为default的可选参数,该参数应该是一个函数。每次JSON试图转换一个值时,它都不知道如何转换,它将调用我们传递给它的函数。函数将接收有问题的对象,并期望返回该对象的JSON表示。
1 2 3 4 5 | def myconverter(o): if isinstance(o, datetime.datetime): return o.__str__() print(json.dumps(d, default = myconverter)) |
最简单的方法是将datetime格式的dict部分更改为isoformat。这个值实际上是一个isoformat中的字符串,JSON可以使用它。
1 2 | v_dict = version.dict() v_dict['created_at'] = v_dict['created_at'].isoformat() |
如果您使用的是python3.7,那么最好的解决方案是使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #!/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() } return super().default(obj) 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)) |
号
输出:
1 2 | {"now": {"_isoformat":"2000-01-01T00:00:00-08:00"}} True |
如果您使用的是python3.6或更低版本,并且只关心时间值(而不是时区),然后可以使用
如果您使用的是python3.6或更低版本,并且您确实关心时区,那么您可以通过
最后,注意所有这些例子的精确性;
如果在视图中使用结果,请确保返回正确的响应。根据API,jsonify执行以下操作:
Creates a Response with the JSON representation of the given arguments
with an application/json mimetype.
号
为了使用json.dumps模拟这种行为,您必须添加一些额外的代码行。
1 2 3 4 | response = make_response(dumps(sample, cls=CustomEncoder)) response.headers['Content-Type'] = 'application/json' response.headers['mimetype'] = 'application/json' return response |
号
您还应该返回dict以完全复制jsonify的响应。所以,整个文件将像这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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和back的完整解决方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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 |
产量
1 2 | {'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文件
1 | {"Date":"2013-11-08 02:30:56.479727","34": 89.9,"45": 67,"12.3": 90,"Extra": 6} |
号
这使我能够导入和导出字符串、int、float和datetime对象。它不应该很难扩展到其他类型。
用一个例子来分析它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #!/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() |
号
通常有几种方法可以序列化日期时间,例如:
如果您同意最后一种方法,json_tricks包将处理日期、时间和日期时间,包括时区。
1 2 3 4 | from datetime import datetime from json_tricks import dumps foo = {'title': 'String', 'datetime': datetime(2012, 8, 8, 21, 46, 24, 862000)} dumps(foo) |
号
它给出:
1 | {"title":"String","datetime": {"__datetime__": null,"year": 2012,"month": 8,"day": 8,"hour": 21,"minute": 46,"second": 24,"microsecond": 862000}} |
号
所以你要做的就是
1 | `pip install json_tricks` |
号
然后从
解码时不将其存储为单个字符串、int或float的好处是:如果您只遇到一个字符串,特别是int或float,那么您需要了解一些关于数据的信息,以了解它是否是日期时间。作为dict,您可以存储元数据以便自动解码,这是
免责声明:这是我做的。因为我也有同样的问题。
其实很简单。如果需要经常序列化日期,那么可以将其作为字符串使用。如果需要,可以很容易地将它们转换回日期时间对象。
如果您主要需要作为日期时间对象工作,那么在序列化之前将它们转换为字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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'> |
如您所见,两种情况下的输出是相同的。只有类型不同。
将
1 | date = str(datetime.datetime(somedatetimehere)) |
。
在用sqlAlchemy编写类内部的序列化装饰器时,收到了相同的错误消息。所以不是:
1 2 3 4 5 6 7 8 | Class Puppy(Base): ... @property def serialize(self): return { 'id':self.id, 'date_birth':self.date_birth, ... } |
。
我只是借用了JGBarah使用isoformat()的想法,并用isoformat()附加了原始值,所以现在它看起来像:
1 2 3 | ... 'date_birth':self.date_birth.isoformat(), ... |
。
如果需要自己的格式设置,可以快速修复
1 2 3 4 | 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) |
。
如果您在通信的两侧,可以使用repr()和eval()函数以及json。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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)) |
。
不应将日期时间导入为
1 | from datetime import datetime |
号
因为埃瓦尔会抱怨。或者可以将datetime作为参数传递给eval。无论如何,这应该是可行的。
我的解决方案…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 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 |
。
好的,现在做一些测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # 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 |
我在将Django模型对象外部化为JSON时遇到了同样的问题。这是你解决问题的方法。
1 2 3 4 5 6 7 8 9 10 | 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 |
号
这个库superjson可以做到。而且,您可以按照以下说明为自己的python对象轻松定制json序列化程序:https://superjson.readthedocs.io/index.html extend。
一般概念是:
您的代码需要基于python对象找到正确的序列化/反序列化方法。通常,完整的类名是一个好的标识符。
然后您的ser/deser方法应该能够将您的对象转换为常规的JSON可序列化对象,这是通用的python类型、dict、list、string、int、float的组合。并反向实现您的deser方法。
1 2 3 | def j_serial(o): # self contained from datetime import datetime, date return str(o).split('.')[0] if isinstance(o, (datetime, date)) else None |
号
上述用途:
1 2 3 4 | import datetime serial_d = j_serial(datetime.datetime.now()) if serial_d: print(serial_d) # output: 2018-02-28 02:23:15 |
号
我可能不是100%正确,但是,这是进行序列化的简单方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #!/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'> |
号