解决“datetime.datetime not JSON serializable”问题

解决“datetime.datetime not JSON serializable”问题

技术背景

在Python中,json 模块用于处理JSON数据的序列化和反序列化。然而,json 模块默认只能处理一些基本的数据类型,如字符串、整数、浮点数、列表、字典等。当尝试将包含 datetime.datetimedatetime.date 对象的字典或其他数据结构进行JSON序列化时,会抛出 TypeError: Object of type datetime is not JSON serializable 异常。这是因为 json 模块不知道如何将这些日期时间对象转换为JSON可接受的格式。

实现步骤

1. 使用 default 参数快速转换

1
2
3
4
5
import json
from datetime import datetime

my_dictionary = {'date': datetime.now()}
print(json.dumps(my_dictionary, indent=4, sort_keys=True, default=str))

此方法使用 default 参数指定一个函数,当 json 模块遇到无法序列化的对象时,会调用该函数。这里使用 str 函数将所有无法序列化的对象转换为字符串,但这种方法在反序列化时可能会有问题。

2. 自定义序列化函数

1
2
3
4
5
6
7
8
9
10
from datetime import date, datetime
import json

def json_serial(obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
raise TypeError("Type %s not serializable" % type(obj))

d = {'date': datetime.now()}
print(json.dumps(d, default=json_serial))

该函数检查对象是否为 datetime.datetimedatetime.date 类型,如果是,则使用 .isoformat() 方法将其转换为ISO 8601格式的字符串。

3. 自定义JSON编码器类

1
2
3
4
5
6
7
8
9
10
11
import json
from datetime import datetime

class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
return json.JSONEncoder.default(self, obj)

sample = {'date': datetime.now()}
print(json.dumps(sample, cls=DateTimeEncoder))

通过继承 json.JSONEncoder 类并重写 default 方法,可以实现自定义的日期时间序列化逻辑。

4. 使用第三方库

orjson

1
2
3
4
5
import orjson
from datetime import datetime

data = {"created_at": datetime(2022, 3, 1)}
print(orjson.dumps(data))

orjson 是一个高性能的JSON库,它对 datetime 对象有很好的支持。

json_tricks

1
2
3
4
5
from datetime import datetime
from json_tricks import dumps

foo = {'title': 'String', 'datetime': datetime(2012, 8, 8, 21, 46, 24, 862000)}
print(dumps(foo))

json_tricks 可以处理日期、时间和带时区的 datetime 对象。

5. Django中的处理方法

1
2
3
4
5
6
7
8
9
10
from django.core.serializers.json import DjangoJSONEncoder
import json
from django.utils.timezone import now

diff = {
"a": 1,
"b": "BB",
"c": now()
}
print(json.dumps(diff, cls=DjangoJSONEncoder))

在Django项目中,可以使用 DjangoJSONEncoder 来处理日期时间对象的序列化。

6. Flask中的处理方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)

sample = {'date': datetime.now()}
response = make_response(dumps(sample, cls=CustomEncoder))
response.headers['Content-Type'] = 'application/json'
response.headers['mimetype'] = 'application/json'
print(response)

在Flask应用中,需要自定义编码器并设置响应头。

核心代码

以下是一个完整的示例,展示了如何使用自定义编码器类来处理日期时间对象的序列化和反序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import json
from datetime import datetime

class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
return json.JSONEncoder.default(self, obj)

def datetime_decoder(d):
for k, v in d.items():
if isinstance(v, str):
try:
d[k] = datetime.fromisoformat(v)
except ValueError:
pass
return d

data = {'date': datetime.now()}
serialized = json.dumps(data, cls=DateTimeEncoder)
print(serialized)

deserialized = json.loads(serialized, object_hook=datetime_decoder)
print(deserialized)

最佳实践

  • 选择合适的序列化格式:ISO 8601格式是一种通用的日期时间表示方式,易于在不同语言和系统之间进行解析,建议优先使用。
  • 使用第三方库:如果项目对性能有较高要求或需要处理复杂的日期时间对象,可考虑使用 orjsonjson_tricks 等第三方库。
  • 封装序列化逻辑:将日期时间序列化逻辑封装在一个函数或类中,方便在项目中复用。

常见问题

1. 反序列化问题

使用 str 函数进行序列化后,反序列化时无法将字符串准确还原为日期时间对象。因此,建议使用 .isoformat() 方法或其他标准化的序列化方式。

2. 时区问题

在处理带时区的日期时间对象时,需要确保序列化和反序列化过程中时区信息的一致性。可以使用 datetime 对象的 tzinfo 属性来处理时区。

3. 性能问题

对于大规模数据的序列化和反序列化,json 模块的性能可能较低。可以考虑使用 orjson 等高性能库来提高性能。


解决“datetime.datetime not JSON serializable”问题
https://119291.xyz/posts/solve-datetime-json-serialization-issue/
作者
ww
发布于
2025年6月26日
许可协议