You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python自定义JSONEncoder时json.dumps()正常但json.dump()日期键报错

问题根源

你重写的是encode()方法,这个方法只有json.dumps()会完整调用:dumps()的执行逻辑是直接调用编码器实例的encode()方法拿到完整字符串结果,所以你在encode()里提前把date类型键转成字符串的逻辑可以正常运行。
但json.dump()走的是增量迭代编码逻辑,它不会直接调用你重写的encode()方法处理整个对象,而是直接调用底层的_iterencode生成器逐块遍历数据写入文件,遍历字典时会先校验键的类型,这时候还没走到你写的键转换逻辑,就直接抛出TypeError了。
另外你之前尝试的default=参数无效是正常现象:default只会在序列化值遇到无法识别的类型时触发,完全不会处理字典的键,所以调整default的实现永远解决不了键类型不合法的问题。

正确修改方案

不要重写encode()方法,改为重写编码器内部被迭代编码逻辑调用的iterencode()方法,让它在开始迭代编码前先完成日期键的转换,这样dumps()和dump()都能正常生效:

import json
import datetime


class JSONSerializer(json.JSONEncoder):
    @staticmethod
    def convert_if_date(_date):
        if isinstance(_date, datetime.date):
            return _date.strftime('%Y-%m-%d')
        return _date

    def date_insensitive_encode(self, obj):
        if isinstance(obj, dict):
            return {self.convert_if_date(k): self.date_insensitive_encode(v) for k, v in obj.items()}
        if isinstance(obj, list):
            return [self.date_insensitive_encode(item) for item in obj]
        if isinstance(obj, tuple):
            return tuple(self.date_insensitive_encode(item) for item in obj)
        return obj

    def iterencode(self, obj, _one_shot=False):
        return super().iterencode(self.date_insensitive_encode(obj), _one_shot=_one_shot)


test_dict = {datetime.date(2022, 7, 10): 'OK'}

# 测试dumps
print(json.dumps(test_dict, cls=JSONSerializer))
# 输出: {"2022-07-10": "OK"}

# 测试dump
with open('dump.json', 'w') as w:
    json.dump(test_dict, w, cls=JSONSerializer)
# 正常写入无报错

注:上面的转换逻辑补了对嵌套字典、列表、元组的递归处理,如果要序列化的结构存在嵌套的日期键也能正常转换,如果只有一层字典可以删掉递归部分简化逻辑。

轻量变通方案(无需自定义编码器)

如果只是单次写入、不需要复用序列化类,可以直接手动转换完字典的键再传给json方法,代码更简洁:

def convert_dict_date_key(obj):
    if isinstance(obj, dict):
        return {
            k.strftime('%Y-%m-%d') if isinstance(k, datetime.date) else k: convert_dict_date_key(v) 
            for k,v in obj.items()
        }
    if isinstance(obj, (list, tuple)):
        return type(obj)(convert_dict_date_key(i) for i in obj)
    return obj

# 直接传入转换后的对象即可
with open('dump.json', 'w') as w:
    json.dump(convert_dict_date_key(test_dict), w)

内容的提问来源于stack exchange,提问作者Plaargath

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 03:12:18