如何实现基于版本选择的JSONDecoder?
解决方案
要实现先读取JSON最外层的version字段再选择对应解码器,最直接的方法是分两步处理:
- 先将JSON内容解析为原生Python字典,提取版本号
- 根据版本号映射选择对应的解码器,重新解析JSON内容为目标对象
具体实现步骤
定义版本-解码器映射字典
把不同版本对应的解码器类统一管理,方便后续扩展:VERSION_DECODER_MAP = { 1.0: Decoder1_0, 2.0: Decoder2_0, # 后续新增版本直接在这里添加映射 }读取文件并提取版本号
先将JSON内容加载为原生字典,获取最外层的version值:input_file = pathlib.Path("your_json_file.json") with open(input_file, 'r', encoding='utf8') as f: content = f.read() # 一次性读取文件内容,避免重复IO # 先解析为原生字典提取版本 raw_data = json.loads(content) version = raw_data.get("version") # 校验版本是否支持 if version not in VERSION_DECODER_MAP: raise ValueError(f"不支持的JSON版本: {version}")使用对应解码器解析内容
根据版本选择解码器,重新解析JSON字符串为目标对象:decoder_cls = VERSION_DECODER_MAP[version] data = json.loads(content, cls=decoder_cls)
修正后的完整代码
import json import pathlib from dataclasses import dataclass @dataclass class Subobj: name: str value: float @dataclass class Obj: subobj: Subobj class Decoder1_0(json.JSONDecoder): def __init__(self, *args, **kwargs): super().__init__(object_hook=self.object_hook, *args, **kwargs) @staticmethod def object_hook(obj): if 'obj' in obj: return Obj(**obj) # 原代码中ObjectA未定义,修正为Obj elif 'subobj' in obj: return Subobj(**obj) return obj # 示例:假设存在Decoder2_0 class Decoder2_0(json.JSONDecoder): def __init__(self, *args, **kwargs): super().__init__(object_hook=self.object_hook, *args, **kwargs) @staticmethod def object_hook(obj): # 这里实现版本2.0的解析逻辑 if 'obj' in obj: return Obj(**obj) elif 'subobj' in obj: return Subobj(**obj) return obj # 版本-解码器映射 VERSION_DECODER_MAP = { 1.0: Decoder1_0, 2.0: Decoder2_0, } input_file = pathlib.Path("your_json_file.json") with open(input_file, 'r', encoding='utf8') as f: content = f.read() # 提取版本号 raw_data = json.loads(content) version = raw_data.get("version") if version not in VERSION_DECODER_MAP: raise ValueError(f"不支持的JSON版本: {version}") # 用对应解码器解析 decoder_cls = VERSION_DECODER_MAP[version] data = json.loads(content, cls=decoder_cls) print(data)
补充说明
- 这种方法避免了递归解析时无法先获取版本号的问题,因为第一步仅解析为原生字典,不会触发自定义解码器的
object_hook - 如果JSON文件极大,一次性读取内容可能占用较多内存,此时可以分两次读取文件:第一次读取到字典拿版本,第二次用解码器加载,内存压力会小一些
- 原代码中
Decoder1_0的object_hook里返回的ObjectA未定义,已修正为Obj,确保代码可运行
内容的提问来源于stack exchange,提问作者Molitoris
相关产品推荐
相关产品推荐

