Python中外部对象序列化为JSON格式的最佳方案咨询
问题说明
调用Client API拉取数据时,执行json.loads(api_response.results)抛出错误:TypeError('Object of type ExternalUnifiedEvent is not JSON serializable');
注:这个报错本质是JSON序列化(Python对象转JSON字符串)时无法识别第三方自定义类型导致的,仅会在调用
json.dumps()时触发,json.loads()是JSON字符串转Python对象的反序列化方法,不会抛出这类错误,此处大概率是方法名笔误。
由于ExternalUnifiedEvent是第三方SDK返回的自定义类型,不在当前代码库定义范围内,因此没有直接调用序列化方法,而是手动拆解对象字段组装字典,后续通过json.dumps()处理后用POST请求发送,需要确认该实现是否为最优方案。
原有实现代码如下:
def get_data(): try: api_response = client_api.get() data = [] for i in api_reponse.results: e = { "event_id": i.event_id "event_type": i.event_type "properties": i.properties } data.append(e) return data except Exception as e: print(e)
headers = {"Content-Type": "application/json",} payload = get_data() response = requests.post(webhook_url, headers=headers, data=json.dumps(payload))
方案评估
手动字段映射的方式可以跑通,但不是最优解,且现有代码存在明显bug:
- 存在低级语法/拼写错误:循环内变量名写为
api_reponse,和上方定义的api_response拼写不一致,运行会直接抛出NameError;字典内的键值对之间缺少逗号分隔,会触发语法错误 - 可维护性差:如果后续SDK返回的
ExternalUnifiedEvent对象新增、删除需要传递的字段,必须手动修改映射逻辑,漏改就会出现字段丢失、字段冗余的问题 - 异常处理逻辑缺失:捕获异常后仅打印错误,没有兜底返回值,上层调用拿到
None后执行json.dumps(payload)会直接崩溃 - 存在重复劳动:如果其他业务逻辑也需要处理
ExternalUnifiedEvent类型对象,需要重复编写字段拆解代码 - 冗余操作:手动调用
json.dumps()、手动设置Content-Type头属于重复劳动,requests库本身自带相关参数可以自动处理
优化实现
优先复用SDK自带的序列化能力,绝大多数第三方SDK的自定义模型都会内置字典转换方法(pydantic模型的model_dump()、普通SDK模型的to_dict()、内置属性__dict__),不需要手动维护字段映射,适配性更强。优化后代码如下:
import json import requests def _serialize_event(event_obj): # 优先调用SDK自带的序列化方法,自动适配字段变更 if hasattr(event_obj, "model_dump"): return event_obj.model_dump(exclude_none=True) if hasattr(event_obj, "to_dict"): return event_obj.to_dict() if hasattr(event_obj, "__dict__"): # 过滤掉对象内置的私有属性 return {k: v for k, v in event_obj.__dict__.items() if not k.startswith("_")} # 兜底逻辑:以上方法都不存在时,再手动映射需要的字段 return { "event_id": event_obj.event_id, "event_type": event_obj.event_type, "properties": event_obj.properties } def get_data(): try: api_response = client_api.get() data = [] # 修复原代码拼写错误 for item in api_response.results: data.append(_serialize_event(item)) return data except Exception as e: print(f"拉取API数据失败: {str(e)}") # 兜底返回空列表,避免上层逻辑崩溃 return [] payload = get_data() if payload: # 直接用requests的json参数,自动完成序列化、设置请求头,不需要手动处理 response = requests.post(webhook_url, json=payload)
优化点说明:
- 抽离独立的序列化方法,全项目可复用,避免重复编写字段映射逻辑
- 优先适配SDK原生序列化能力,后续SDK字段更新不需要手动修改代码,自动同步全量合法字段
- 修复了原代码的拼写错误、语法错误
- 异常处理增加兜底返回值,避免上层逻辑因接口拉取失败崩溃
- 移除冗余的手动序列化、手动设置请求头操作,用
requests内置的json参数自动处理,减少人为失误 - 增加空payload判断,避免发送无意义的空请求
内容的提问来源于stack exchange,提问作者fizzbuzzzzz
相关产品推荐
相关产品推荐

