You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 09:07:29