如何让cattrs递归解构嵌套结构并将date/datetime转为字符串?
解决cattrs递归序列化嵌套结构中date/datetime为字符串的问题
问题场景
你用attrs定义了嵌套的数据结构,尝试用cattrs的钩子处理date和datetime类型的序列化/反序列化,但执行unstructure后,嵌套结构中的date和datetime对象并未转为预期的字符串格式:
定义的结构类
from datetime import datetime, date import attrs from typing import List, Optional, Dict @attrs.define class Score: added: datetime value: float @attrs.define class Entry: score: Score tags: List[str] added: Optional[datetime] = None @attrs.define class Entries: entries: Dict[date, Entry] = attrs.Factory(dict)
原始数据
data = { 'entries': { '2024-01-01': { 'score': {'added': '2023-02-03 04:03:00', 'value': 80.3}, 'tags': ['meatball', 'salami', 'jerky'], }, # … more days } }
错误的钩子注册
import cattrs # 钩子逻辑完全颠倒 cattrs.register_unstructure_hook(datetime, lambda dt: datetime.strptime(dt, '%Y-%m-%d %H:%M:%S')) cattrs.register_structure_hook(datetime, lambda dt, _: str(dt)) cattrs.register_unstructure_hook(date, lambda dt: datetime.strptime(dt, '%Y-%m-%d').date()) cattrs.register_structure_hook(date, lambda dt, _: str(dt))
不符合预期的unstructure结果
structured = cattrs.structure(data, Entries) # 输出仍包含date/datetime对象,未转为字符串 cattrs.unstructure(structured) == { 'entries': { datetime.date(2024, 1, 1): { 'score': { 'added': datetime.datetime(2023, 2, 3, 4, 3), 'value': 80.3 }, 'tags': ['meatball', 'salami', 'jerky'], 'added': None } } }
问题原因
你注册的unstructure和structure钩子逻辑完全搞反了:
unstructure_hook的作用是将Python对象(如datetime/date实例)转换为可序列化的原始类型(如字符串),但你误用了datetime.strptime(该方法是把字符串解析为datetime对象)。structure_hook的作用是将原始数据(如字符串)转换为Python对象,但你误用了str(dt)(该方法是把对象转为字符串)。
解决方案
修正钩子的逻辑,让structure_hook负责解析字符串为对象,unstructure_hook负责将对象转为字符串:
# 正确注册钩子 # structure钩子:将字符串解析为datetime/date对象 cattrs.register_structure_hook(datetime, lambda s, _: datetime.strptime(s, '%Y-%m-%d %H:%M:%S')) cattrs.register_structure_hook(date, lambda s, _: datetime.strptime(s, '%Y-%m-%d').date()) # unstructure钩子:将datetime/date对象格式化为字符串 cattrs.register_unstructure_hook(datetime, lambda dt: dt.strftime('%Y-%m-%d %H:%M:%S')) cattrs.register_unstructure_hook(date, lambda d: d.strftime('%Y-%m-%d'))
验证结果
执行以下代码验证:
structured = cattrs.structure(data, Entries) unstructured = cattrs.unstructure(structured) # 预期结果(所有date/datetime均转为字符串) expected = { 'entries': { '2024-01-01': { 'score': { 'added': '2023-02-03 04:03:00', 'value': 80.3 }, 'tags': ['meatball', 'salami', 'jerky'], 'added': None } } } print(unstructured == expected) # 输出True
cattrs的钩子会递归处理嵌套结构中的所有对应类型,无需额外配置递归逻辑,只要钩子定义正确即可自动完成所有嵌套对象的序列化。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

