为啥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序列化不能为日期时间对象开箱即用的主要内容,如果未能解决你的问题,请参考以下文章
Python 和 JavaScript 之间的 JSON 日期时间
为啥 UserAuthExtensions.PopulateFromMap(session, jwtPayload) 不能在 ServiceStack.Auth 中正确反序列化带有转义的 json 值