Python数据类转换时strptime()参数错误排查:JSON转类型化对象列表失败
我太懂这种踩坑的感觉了——好不容易把数据类字段和JSON结构一一对应好,结果卡在strptime的None值错误上,简直头疼。咱们先把问题拆解开,一步步解决。
问题根源定位
你虽然给可选日期字段加了Optional[DateTimePattern[...]],但dataclass-wizard的DateTimePattern类型在处理None值时,内部逻辑还是会尝试把None传给strptime方法,而strptime只接受字符串参数,自然就抛出类型错误了。说白了就是:Optional标记告诉Python这个字段可以是None,但DateTimePattern的解析器没跟上这个逻辑,还是硬要解析None值。
另外,我注意到你贴的第二条JSON记录最后last_analytics_received_at字段是截断的,记得先确保所有JSON数据是完整有效的,不然还没到日期解析步骤可能就先报错了。
具体解决方法
1. 先升级依赖(最快速的尝试)
dataclass-wizard的老版本对Optional[DateTimePattern]的支持确实有bug,先把包更到最新版,大概率能直接解决问题:
pip install --upgrade dataclass-wizard
2. 调整数据类字段定义(更稳妥的方案)
如果升级后还是不行,咱们换个思路:用原生的datetime类型代替DateTimePattern,配合JSONWizard的全局日期格式配置,这样解析器会自动处理None值。修改后的字段定义如下:
from dataclasses import dataclass from dataclass_wizard import fromlist, JSONWizard from datetime import datetime from typing import Optional @dataclass class FotaDevice(JSONWizard): imei:int serial:int model:str spec_id:int platform_id:int current_configuration:Optional[str] current_firmware:str description:Optional[str] company_id:int group_id:Optional[int] track:int # 把所有Optional[DateTimePattern]换成Optional[datetime] first_login:Optional[datetime] = None seen_at:Optional[datetime] = None created_at:datetime updated_at:Optional[datetime] = None state_timestamp:Optional[datetime] = None created_by:int updated_by:Optional[int] chip_id:Optional[int] gsm_number:Optional[str] gnss_version:Optional[str] login_count:int parameter_collect:Optional[str] last_sync:datetime iccid:str imsi:str ble_firmware:Optional[str] # 注意:JSON里该字段为null,之前的str类型会报错 sold_at:datetime shipment:str last_analytics_received_at:Optional[datetime] = None product_code:str company_name:Optional[str] group_name:Optional[str] next_task:Optional[str] can_adapter:Optional[str] obd:Optional[str] accessories:Optional[str] carrier:Optional[str] company:Optional[dict] # 如果你定义了FotaCompany类,这里换成Optional[FotaCompany] group:Optional[dict] activity_status:str task_queue:str modem_version:Optional[str] model_platform:str # 全局配置日期格式,不用每个字段单独写 class Meta: datetime_format = '%Y-%m-%d %H:%M:%S'
3. 自定义解析逻辑(终极兜底方案)
如果上面两种方法都不管用,咱们就手动接管日期解析,确保None值直接跳过:
from dataclasses import dataclass from dataclass_wizard import fromlist, JSONWizard from datetime import datetime from typing import Optional @dataclass class FotaDevice(JSONWizard): # 所有字段定义和上面一致... @classmethod def from_dict(cls, data: dict): DATE_FORMAT = '%Y-%m-%d %H:%M:%S' # 处理可选日期字段:值为None就跳过,否则解析成datetime optional_date_fields = [ 'first_login', 'seen_at', 'updated_at', 'state_timestamp', 'last_analytics_received_at' ] for field in optional_date_fields: value = data.get(field) if value is not None: data[field] = datetime.strptime(value, DATE_FORMAT) # 处理必填日期字段:确保一定有值,直接解析 required_date_fields = [ 'created_at', 'last_sync', 'sold_at' ] for field in required_date_fields: data[field] = datetime.strptime(data[field], DATE_FORMAT) # 调用父类方法完成剩余字段的解析 return super().from_dict(data)
最后验证
修改完成后,再运行你的转换代码:
result_list = fromlist(FotaDevice, intermediate_list)
记得先打印intermediate_list里的每个元素,确认所有日期字段要么是符合格式的字符串,要么是None,没有截断或格式错误的值。
内容来源于stack exchange

