JSON序列化器类-为什么JSON .dumps()工作,而JSON .dump()不?



我想要json.dump()一个具有datetime.date类型键的字典,为此,我制作了一个序列化器类来将date键编码为字符串。令我惊讶的是,它适用于json.dumps(),但抛出一个错误与json.dump()。最后,我能够使用解决方案转储,但我想了解这里发生了什么:为什么序列化器类与json.dumps()一起工作,而不与json.dump()一起工作?

我试着写一个类似的函数来使用default=而不是cls=,试着来回重写这个,试着适应我在网上发现的一些例子,但我总是以同样的错误告终。我不知道我在这里错过了什么,即使在阅读了文档之后。请告诉我,我需要在这里做些什么更改,以便能够序列化日期和转储字典。

下面是MRE (Python 3.8.6):

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): v for k, v in obj.items()}
return obj
def encode(self, obj):
return super(JSONSerializer, self).encode(
self.date_insensitive_encode(obj))

test_dict = {datetime.date(2022, 7, 10): 'OK'}
print(json.dumps(test_dict, cls=JSONSerializer))
# this prints: {"2022-07-11": "OK"}
with open('dump.json', 'w') as w:
json.dump(test_dict, w, cls=JSONSerializer)
# but this throws a TypeError

完全错误:

Traceback (most recent call last):
File "E:/Progz/Python/AppKH/datacontrol/so_mre.py", line 28, in <module>
json.dump(test_dict, w, cls=JSONSerializer)
File "C:Program FilesPython38libjson__init__.py", line 179, in dump
for chunk in iterable:
File "C:Program FilesPython38libjsonencoder.py", line 431, in _iterencode
yield from _iterencode_dict(o, _current_indent_level)
File "C:Program FilesPython38libjsonencoder.py", line 376, in _iterencode_dict
raise TypeError(f'keys must be str, int, float, bool or None, '
TypeError: keys must be str, int, float, bool or None, not date

根据以前的经验,我认为基础json.JSONEncoder类的encode()方法仅在调用dumps()方法时使用,而不是在dump()时使用,但这显然是而不是这种情况下(所以我以前的解决方案不适用)…但好消息是,我能够做我认为你想要做的只是在一个不同的,更简单的方式与两种方法的工作-它只需要重写encode()方法这样做。

from collections.abc import MutableMapping
import json
import datetime

class JSONSerializer(json.JSONEncoder):
def encode(self, obj):
# Convert dictionary keys that are datatime.dates into strings.
if isinstance(obj, MutableMapping):
for key in list(obj.keys()):
if isinstance(key, datetime.date):
strkey = key.strftime('%Y-%m-%d')
obj[strkey] = obj.pop(key)
return super().encode(obj)

test_dict = {datetime.date(2022, 7, 10): 'OK'}
print(json.dumps(test_dict, cls=JSONSerializer))  # -> {"2022-07-11": "OK"}
with open('dump.json', 'w') as w:
json.dump(test_dict, w, cls=JSONSerializer)  # # Works now, too.