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

Symfony 6中如何将DateTime数组反序列化为DateTime对象?

解决Symfony 6中Historique对象反序列化问题的方案

问题根源

你的createdAt字段是数组格式(包含date、timezone_type、timezone),但默认的DateTimeNormalizer只处理字符串格式的日期,而ObjectNormalizer无法自动将这种数组转换为DateTime对象,导致两种顺序的配置都报错。

可行方案

方案一:使用CallbackNormalizer预处理日期数组

通过CallbackNormalizer在反序列化前将createdAt数组手动转换为DateTime对象,再交给后续归一化器处理:

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\CallbackNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;

$normalizers = [
    new CallbackNormalizer(
        // 反序列化前的预处理回调
        function ($data, $type, $format, $context) {
            if ($type === Historique::class && isset($data['createdAt'])) {
                // 将数组格式的日期转换为DateTime对象
                $data['createdAt'] = \DateTime::createFromFormat(
                    'Y-m-d H:i:s.u', // 匹配带微秒的日期格式
                    $data['createdAt']['date'],
                    new \DateTimeZone($data['createdAt']['timezone'])
                );
            }
            return $data;
        }
    ),
    new ObjectNormalizer(null, null, null, new ReflectionExtractor()),
    new BackedEnumNormalizer(),
    new DateTimeNormalizer(), // 保留以处理其他字符串格式的日期
];

$serializer = new Serializer($normalizers, [new JsonEncoder()]);
// 执行反序列化
$historiqueObject = $serializer->denormalize($historiqueArray, Historique::class);

方案二:使用注解回调处理日期字段

在Historique类中添加反序列化回调注解,指定createdAt字段的转换逻辑:

  1. 给Historique类添加注解和回调方法:
use Symfony\Component\Serializer\Annotation\DenormalizeCallback;

class Historique
{
    #[DenormalizeCallback(callback: [self::class, 'denormalizeCreatedAt'])]
    public \DateTime $createdAt;

    public function __construct(public readonly string $utilisateur, public readonly string $type, public readonly ?string $texte)
    {
        $this->createdAt = new \DateTime();
    }

    // 静态回调方法:将数组转为DateTime
    public static function denormalizeCreatedAt(array $dateData): \DateTime
    {
        return \DateTime::createFromFormat(
            'Y-m-d H:i:s.u',
            $dateData['date'],
            new \DateTimeZone($dateData['timezone'])
        );
    }

    public function getTypeLabel(): string
    {
        return HistoriqueTypeEnum::getLabelName($this->type);
    }
}
  1. 配置序列化器时添加AnnotationNormalizer:
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\AnnotationNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;

$normalizers = [
    new AnnotationNormalizer(), // 优先处理注解回调
    new DateTimeNormalizer(),
    new ObjectNormalizer(null, null, null, new ReflectionExtractor()),
    new BackedEnumNormalizer(),
];

$serializer = new Serializer($normalizers, [new JsonEncoder()]);
$historiqueObject = $serializer->denormalize($historiqueArray, Historique::class);

关键注意点

  • 日期格式要匹配输入的date字段:输入包含微秒(.209330),所以必须用Y-m-d H:i:s.u格式,不能用你之前的Y-m-d H:i:s,否则会解析失败。
  • 归一化器顺序很重要:处理日期转换的逻辑(CallbackNormalizer或AnnotationNormalizer)必须放在ObjectNormalizer前面,确保createdAt在被赋值给对象前已经转换为DateTime对象。

内容的提问来源于stack exchange,提问作者eldiablo62

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 18:33:16