为啥python中日期时间对象的json序列化不能为日期时间对象开箱即用

Posted

技术标签:

【中文标题】为啥python中日期时间对象的json序列化不能为日期时间对象开箱即用【英文标题】:Why does json serialization of datetime objects in python not work out of the box for datetime objects为什么python中日期时间对象的json序列化不能为日期时间对象开箱即用 【发布时间】:2012-05-30 02:06:34 【问题描述】:

为什么 json 序列化不适用于 datetime 对象。据我了解 json 序列化,任何对象的基本思想都可以调用 __str__ 内置函数,然后对作为响应的对象进行 urlencode。但是在日期时间的情况下,我会收到以下错误

TypeError: datetime.datetime(2012, 5, 23, 18, 38, 23, 37566) is not JSON serializable

虽然有__str__ 即一种对已经可用的对象进行字符串化的方法,但似乎有意识地决定不这样做,为什么会这样?

【问题讨论】:

您误解了 json 的工作原理。它与 str 方法无关。 JSON 没有日期时间类型,周期,因此不可能在 JSON 中无损编码日期时间,而在接收端没有某种特殊逻辑。因此,该库(逻辑上)通过转换为 Unix 时间戳或 ISO 日期字符串或其他内容并明确说明转换是必要的,让您自己完成。 @TylerEaves 这一点都不合逻辑。可以将日期时间无损编码为字符串或 int,并且许多用例需要从 dict 转换为 json,再到 dict 永远不会离开 python 生态系统,但是 json 模块无法在没有自定义的情况下处理这种情况日期时间处理程序?真的?!查看有关该主题的大量 *** 问题,我想说我并不孤单。 【参考方案1】:

不,它在json 模块中不起作用。该模块为您提供了一个默认编码器: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)

正如其他人正确指出的那样,原因是standard for json 没有指定如何表示日期时间。

【讨论】:

如何在前端解码这个日期时间?我在问这个小数的解码。 @Clayton,您将在 JSON 响应中得到的是 unix 时间戳。 This question 回答如何将 unix 时间戳转换为日期。 @Vikas 谢谢。我遵循了您的代码,但我将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() 添加到末尾。【参考方案2】:

如果你想在不实现的情况下获得日期时间的编码和解码,你可以使用json_tricks,它是一个为各种流行类型添加编码和解码的包装器。只需安装:

pip install json_tricks

然后从json_tricks而不是json导入,例如:

from json_tricks import dumps, loads
json = dumps('name': 'MyName', 'birthday': datetime.datetime(1992, 5, 23, 18, 38, 23, 37566))
me = loads(json)

免责声明:它是由我制作的。因为我遇到了同样的问题。


如果你想自动序列化任何可以字符串化的东西,你可以很容易地用标准实现来做到这一点:

dumps(obj, default=str)

但请注意,这有缺点,例如没有额外的努力,它们都不会被反序列化,也许有时你只是不想序列化某些东西(比如一个大的 numpy 数组的函数),而是得到一个警告,这个方法会静音。

【讨论】:

很高兴提到 default=str。最常见的情况下最简单的解决方法。【参考方案3】:

修补 json 模块以使序列化支持日期时间的简单方法。

import json
import datetime

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

不像往常那样使用 json 序列化 - 这次将 datetime 序列化为 isoformat。

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

导致:'"created": "2015-08-26T14:21:31.853855"'

在以下位置查看更多详细信息和一些注意事项: ***: JSON datetime between Python and javascript

【讨论】:

我见过的最佳解决方案。一行补丁。【参考方案4】:

您希望它们如何被序列化?

JSON 没有指定如何处理日期,因此 python json 库无法决定如何为您表示这些日期。这完全取决于另一方(浏览器、脚本等)如何处理 JSON 格式的日期。

【讨论】:

使用默认序列化,不一定在意。您只需要能够进入 json 格式并退出而不会有太多麻烦。当然,完全在日期时间上失败肯定是错误的答案。 那就是需要定制的时候了。如果我只是从 python 到 json 再到 python,开箱即用,默认情况下不处理常见数据类型只会增加不必要的开销。另外,那些其他语言也有标准的 json 转换,所以可以简单地从它作为一个好的默认值开始。 不,没有这样的默认值。该标准没有指定任何内容。 这会阻止 Python 编码日期时间,为什么?其他语言没有这样的问题。 Json 的数据抽象使其更加 类型更灵活,而不是更灵活。大多数语言(为自己)选择默认的日期时间字符串编码,然后至少能够在语言中一致地读取和写入 json 的日期时间值。 Python 不仅强制与外部系统不兼容,而且即使是两个不同的 Python 应用程序也可能以不同的方式处理 datetime/json。 选择一种格式并不难,但这并不是必须做的全部。例如,让 sqlalchemy 与填充到 postgresql JSON 列中的字典(包含各种嵌套级别的日期时间)一起工作并非易事。 go、ruby 和 javascript 中的一项微不足道的任务。毕竟这只是字典/哈希序列化,但 python 做不到。 Python确实选择了从字符串创建日期时间可接受的格式,并且也有通用标准,因此在这种情况下,“python 哲学”似乎充其量是循环逻辑。

以上是关于为啥python中日期时间对象的json序列化不能为日期时间对象开箱即用的主要内容,如果未能解决你的问题,请参考以下文章

无法反序列化当前的JSON对象,为啥

Python 和 JavaScript 之间的 JSON 日期时间

json为啥解析为javabean

为啥 UserAuthExtensions.PopulateFromMap(session, jwtPayload) 不能在 ServiceStack.Auth 中正确反序列化带有转义的 json 值

Spring-boot @RequestBody JSON 到带有日期反序列化示例的对象?

为啥不能从文件中解析 JSON?