如何将Python对象写入JSON文件?嵌套类对象序列化报错求助
咱先把报错的根源说清楚:Python自带的json模块天生只认识原生数据类型——比如字典、列表、字符串、数字这些常规玩意儿。你定义的Process、Lanes、Tasks都是自己造的类实例,json模块哪见过这些啊?它完全不知道该怎么把这些对象转成JSON格式的字符串,所以直接给你抛了Object of type 'Process' is not JSON serializable这个错误。
下面给你三种实用的解决办法,从简单到灵活,你按需选:
方法一:用__dict__快速转字典(上手最快)
Python的类实例都自带一个__dict__属性,它会把对象里所有的实例变量打包成一个字典。你只要把自定义对象转成这个字典,json模块就能认了。
比如你原来的代码可能是这样:
import json # 假设你已经创建了一个Process实例 my_process = Process(...) with open('process_output.json', 'w') as f: json.dump(my_process, f)
改成这样就行:
import json my_process = Process(...) # 先把Process对象转成字典 process_dict = my_process.__dict__ # 因为你的结构是嵌套的,Lanes和Tasks也是自定义类,得逐层转 process_dict['lanes'] = [lane.__dict__ for lane in process_dict['lanes']] # 再把每个Lanes里的Tasks也转成字典列表 for lane_item in process_dict['lanes']: lane_item['tasks'] = [task.__dict__ for task in lane_item['tasks']] # 现在就能正常写入JSON了,加个indent=4让格式更好看 with open('process_output.json', 'w') as f: json.dump(process_dict, f, indent=4)
这个方法的优点是零额外代码,上手快;缺点是如果你的类里有私有变量(比如以_开头的),也会被一起序列化进去,要是不想让这些内容出现在JSON里,就得换别的方法。
方法二:自定义JSON编码器(适合嵌套结构)
如果你的嵌套层级比较多,手动转__dict__太麻烦,那可以自己写个编码器,让json模块自动识别你的自定义类。
代码示例:
import json # 继承JSONEncoder写自己的编码器 class CustomJSONEncoder(json.JSONEncoder): def default(self, obj): # 只要是你的自定义类实例,就返回它的__dict__ if isinstance(obj, (Process, Lanes, Tasks)): return obj.__dict__ # 其他类型交给父类处理,避免破坏原生类型的序列化 return super().default(obj) # 使用的时候指定cls参数就行,不用手动转嵌套对象 my_process = Process(...) with open('process_output.json', 'w') as f: json.dump(my_process, f, cls=CustomJSONEncoder, indent=4)
这种方法一劳永逸,不管嵌套多少层,只要是你指定的自定义类,都会自动被转成字典,非常适合你这种Process包含Lanes数组、Lanes包含Tasks数组的结构。
方法三:给类加__json__方法(最规范可控)
如果想精准控制每个类要序列化哪些字段,甚至对字段做一些转换(比如把日期转成特定格式的字符串),可以给每个自定义类加一个__json__方法,专门返回要序列化的内容。
示例代码:
class Process: def __init__(self, lanes, process_name, create_time): self.lanes = lanes self.process_name = process_name self.create_time = create_time # 假设是datetime对象 self._private_var = "不想序列化的内容" # 私有变量 def __json__(self): # 只返回你想要的字段,还能做转换,比如把datetime转成字符串 return { 'process_name': self.process_name, 'create_time': self.create_time.strftime("%Y-%m-%d %H:%M:%S"), 'lanes': [lane.__json__() for lane in self.lanes] } class Lanes: def __init__(self, tasks, lane_id): self.tasks = tasks self.lane_id = lane_id def __json__(self): return { 'lane_id': self.lane_id, 'tasks': [task.__json__() for task in self.tasks] } class Tasks: def __init__(self, task_name, task_status): self.task_name = task_name self.task_status = task_status def __json__(self): return { 'task_name': self.task_name, 'task_status': self.task_status }
然后序列化的时候,调用__json__()拿到字典再写入:
import json my_process = Process(...) with open('process_output.json', 'w') as f: json.dump(my_process.__json__(), f, indent=4)
这种方式最规范,能完全掌控序列化的内容,后期维护也方便,适合长期迭代的项目。
内容的提问来源于stack exchange,提问作者Adrian Coutsoftides

