Symfony中DateTime类型反序列化失败,如何解决?
问题分析
你的问题根源在于三个核心点:
- Normalizer功能局限性:使用的
GetSetMethodNormalizer是基础属性访问器,只会直接调用setter方法,不会根据属性类型自动委托DateTimeNormalizer完成字符串到DateTimeInterface的转换。 - 属性命名不匹配:JSON字段是蛇形命名
date_of_birth,但类属性是驼峰命名dateOfBirth,两者无法自动映射。 - 注解未被解析:
#[Context]注解需要Serializer通过注解读取器识别,否则配置的日期格式规则不会生效。
解决方案
1. 替换为ObjectNormalizer(推荐)
ObjectNormalizer支持类型解析、注解读取和自动委托对应类型的Normalizer,能解决类型转换问题。修改Serializer初始化代码:
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use Doctrine\Common\Annotations\AnnotationReader; // 初始化注解读取器,识别#[Context]等注解 $classMetadataFactory = new ClassMetadataFactory( new AnnotationLoader(new AnnotationReader()) ); $serializer = new Serializer( [ new ArrayDenormalizer(), new DateTimeNormalizer(), new ObjectNormalizer($classMetadataFactory), ], [new JsonEncoder()] );
2. 处理属性命名映射
针对JSON蛇形、类属性驼峰的命名差异,给ObjectNormalizer添加命名转换器:
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; // ... 其他代码 $normalizer = new ObjectNormalizer( $classMetadataFactory, new CamelCaseToSnakeCaseNameConverter() ); $serializer = new Serializer( [ new ArrayDenormalizer(), new DateTimeNormalizer(), $normalizer, ], [new JsonEncoder()] );
3. 单类测试示例修改(完整可运行)
<?php namespace App\Controller; use DateTimeInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Serializer\Annotation\Context; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use Doctrine\Common\Annotations\AnnotationReader; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; class TestController extends AbstractController { #[Route(path: '/test', name: 'app_test')] public function index(): Response { $json = '{"date_of_birth":"2023-01-31"}'; $classMetadataFactory = new ClassMetadataFactory( new AnnotationLoader(new AnnotationReader()) ); $serializer = new Serializer( [ new ArrayDenormalizer(), new DateTimeNormalizer(), new ObjectNormalizer( $classMetadataFactory, new CamelCaseToSnakeCaseNameConverter() ), ], [new JsonEncoder()] ); $testPersonFromJson = $serializer->deserialize($json, TestPerson::class, 'json'); return $this->json($testPersonFromJson); } } class TestPerson { #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])] private ?DateTimeInterface $dateOfBirth = null; public function getDateOfBirth(): ?DateTimeInterface { return $this->dateOfBirth; } public function setDateOfBirth(?DateTimeInterface $dateOfBirth): self { $this->dateOfBirth = $dateOfBirth; return $this; } }
4. 关键注意事项
- Normalizer顺序:
ArrayDenormalizer必须放在最前面(处理数组类型反序列化),DateTimeNormalizer要在ObjectNormalizer之前,确保对象属性处理时能找到对应的类型转换工具。 - 依赖安装:若缺少
doctrine/annotations包,执行composer require doctrine/annotations以支持注解读取。 - 日期格式匹配:确保JSON日期字符串与
#[Context]配置的Y-m-d格式完全一致,否则会抛出转换异常。
内容的提问来源于stack exchange,提问作者Krann Sock
相关产品推荐
相关产品推荐

