为什么Python中的JSON序列化对于datetime对象不能开箱即用?
为什么Python中的JSON序列化无法直接处理datetime对象?
问题的出现原因是因为Python的json模块默认情况下无法直接将datetime对象序列化为JSON格式。然而,我们可以通过简单地对json模块进行一行代码的修补来解决这个问题。
解决方法是使用以下代码对json模块进行修补:
import json import datetime json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)
通过将这行代码添加到代码中,我们可以使json模块支持对datetime对象的序列化。修补后的json模块将会将datetime对象转换为isoformat格式。
使用修补后的json模块进行序列化的方式与平常一样,只不过这次datetime对象会被序列化为isoformat格式:
json.dumps({'created':datetime.datetime.now()})
运行上述代码后,我们将得到如下结果:
'{"created": "2015-08-26T14:21:31.853855"}'
如果想要了解更多细节以及一些建议,请参考以下链接:
[StackOverflow: JSON datetime between Python and JavaScript](https://stackoverflow.com/questions/455580/32224522#32224522)
这是我见过的最好的解决方法,只需要一行代码的修补就能解决问题。
为什么Python中的datetime对象的JSON序列化不能直接使用?这个问题的出现原因是因为JSON并没有明确指定如何处理日期,所以Python的json库无法决定如何代表日期。这完全取决于其他一方(浏览器、脚本等)在JSON中如何处理日期。
对于默认的序列化,你可能并不关心具体的表示方式。你只需要能够方便地将数据转换成JSON格式并重新转换回来。对于datetime完全无法处理肯定是错误的。
然而,在交换格式中,你需要关心这个问题。Java或JavaScript应用程序中的JSON库如何处理日期?
这时候就需要进行自定义了。默认情况下,如果只是从Python到JSON再到Python,不对常见的数据类型进行默认处理只会增加不必要的开销。另外,其他语言也有标准的JSON转换方式,所以可以从那个标准开始作为一个好的默认方式。
然而,并没有这样的默认方式。标准中并没有指定任何方式。
这导致Python无法编码日期对象吗?其他语言并没有这样的问题。JSON的数据抽象使其更加类型灵活,而不是相反。大多数语言(对于自身来说)选择了一种默认的日期时间字符串编码方式,并且至少能够在语言内部一致地读写日期时间值。Python不仅与外部系统不兼容,甚至两个不同的Python应用程序可能以不同的方式处理日期时间/JSON。
但是这里没有互操作性。Python的哲学包括“拒绝猜测”和“明确优于隐式”。没有标准就没有明智的默认方式,Python在这里拒绝猜测并强制你明确选择一种方式。JSON的大多数用例是与其他“未知”客户端进行通信,即使它们有一个默认的日期时间序列化方式,它们对格式也有“不同的想法”。
选择一个格式并不难,但这不是你需要做的全部。举个例子,让sqlalchemy与嵌套在postgresql JSON列中的包含不同嵌套级别的字典(包含日期时间)一起工作是非平凡的。对于Go、Ruby和JavaScript来说,这只是一个简单的字典/哈希序列化任务。然而Python却无法做到这一点。Python在从字符串创建日期时间方面确实做出了选择,并且还有一些通用标准,所以在这种情况下,“Python的哲学”似乎最多是循环逻辑。
如果json.dumps可以自动编码日期时间,那么json.loads就需要自动解码。因此,JSON字符串始终是Python字符串,除非它们以某种方式格式化,以便json.loads决定它们是日期时间,然后它们才变成日期时间。即使它们一开始只是字符串。
这正是JSON的工作方式。上下文定义了6种JSON类型的含义。没有上下文,字符串只是一个字符串;有了上下文,你就知道你期望的类型是什么。在JSON中,函数对称性并不重要,功能才是最重要的。
为什么Python中的datetime对象在JSON序列化时不能直接工作?
JSON模块中的默认编码器`json.JSONEncoder`并不支持直接对datetime对象进行序列化。为了解决这个问题,我们需要扩展`json.JSONEncoder`,并提供自己的`default`方法来序列化对象。
下面是一个示例代码:
import json import datetime from time import mktime class MyEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, datetime.datetime): return int(mktime(obj.timetuple())) return json.JSONEncoder.default(self, obj) print json.dumps(obj, cls=MyEncoder)
如其他人正确指出的,问题的原因是JSON的标准并没有规定如何表示日期时间。
在JSON响应中,你将得到一个Unix时间戳。可以参考这个问题来学习如何将Unix时间戳转换为日期时间。
感谢你的建议。我遵循了你的代码,并稍微修改了`isinstance`检查部分,如下所示:
if isinstance(obj, datetime.datetime): return obj.strftime('%Y/%m/%d/%H/%M/%S') elif isinstance(obj, datetime.date): return obj.strftime('%Y/%m/%d')
使用`mktime`确实可以工作,但我发现了一种更好的方法。JSON使用ISO 8601格式来表示时间戳。要在Python中获得相同类型的时间格式,只需在datetime对象后面添加`.isoformat()`即可。