在大多数情况下,这是一个双向问题,如果您使用自定义编码器,您可能需要自定义解码器(反之亦然)。在这种情况下,解码器应该能够解析编码数据并返回原始 json 对象。
下面有一个完整的练习,使用 2 种不同的策略将 python 不可序列化的对象转换为 json:
- 修补 JSONEncoder 类以序列化任何实现“json”方法以序列化类的类。
- 使用“转换器”方法列表来序列化特定的 Python 类型。
在下面的示例中,我使用自定义 json 方法将 Enum 类序列化为 {enum.name: enum.value} dict,这里 enun.value 对象在 python 中是不可序列化的类型(日期和元组),通过使用列出的 CONVERTERS 方法,我们可以将这些类型转换为可序列化类型。
编码后,可以调用 custom_json_decoder 方法将该 json 转换回 python 原始类型。
下面这个脚本示例是完整的,它应该“按原样”运行:
from enum import Enum
from dateutil.parser import parse as dtparse
from datetime import datetime
from datetime import date
from json import JSONEncoder
from json import loads as json_loads
from json import dumps as json_dumps
def wrapped_default(self, obj):
json_parser = getattr(obj.__class__, "__json__", lambda x: x.__dict__)
try:
return json_parser(obj)
except Exception:
return wrapped_default.default(obj)
wrapped_default.default = JSONEncoder().default
JSONEncoder.default = wrapped_default
CONVERTERS = {
"datetime": dtparse,
"date": lambda x: datetime.strptime(x, "%Y%m%d").date(),
"tuple": lambda x: tuple(x),
}
class RskJSONEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, date):
return {"val": obj.strftime("%Y%m%d"), "pythontype": "date"}
elif isinstance(obj, datetime):
return {"val": obj.isoformat(), "pythontype": "datetime"}
elif isinstance(obj, tuple):
return {"val": list(obj), "pythontype": "tuple"}
return super().default(obj)
def custom_json_decoder(obj):
def json_hook(json_obj):
try:
return CONVERTERS[json_obj.pop("pythontype")](json_obj["val"])
except Exception:
res = json_obj
return res
return json_loads(obj, object_hook=json_hook)
def custom_json_encoder(obj):
return json_dumps(obj, cls=RskJSONEncoder)
if __name__ == "__main__":
class Test(Enum):
A = date(2021, 1, 1)
B = ("this", " is", " a", " tuple")
def __json__(self):
return {self.name: self.value}
d = {"enum_date": Test.A, "enum_tuple": Test.B}
this_is_json = custom_json_encoder(d)
this_is_python_obj = custom_json_decoder(this_is_json)
print(f"this is json, type={type(this_is_json)}\n", this_is_json)
print(
f"this is python, type={type(this_is_python_obj)}\n",
this_is_python_obj,
)