Symfony 6 Serializer组件反序列化嵌套JSON报错求助
解决Symfony Serializer反序列化嵌套JSON为PHP对象的报错问题
问题场景
使用Symfony Serializer组件将嵌套JSON响应转换为PHP类对象,JSON结构如下:
{"data": {"uuid": "d1830cc0-f35a-11e8-8ca1-5254003b69f1","name": "***","last_name": "Admin","fullname": "***","email": "***@***.com","created_at": {"time24": "16:13","time": "04:13","date": "2018-11-28","datetime": "2018-11-28 16:13:22","meta": {"meridiem": "pm","day": 28,"month": 11,"year": 2018,"month_name": "November"}},"updated_at": {"time24": "16:13","time": "04:13","date": "2018-11-28","datetime": "2018-11-28 16:13:22","meta": {"meridiem": "pm","day": 28,"month": 11,"year": 2018,"month_name": "November"}},"relationships": {"role": {"uuid": "d14ee40e-f35a-11e8-bbfc-5254003b69f1","name": "admin","label": "Admin","public": true,"created_at": {"time24": "16:13","time": "04:13","date": "2018-11-28","datetime": "2018-11-28 16:13:21","meta": {"meridiem": "pm","day": 28,"month": 11,"year": 2018,"month_name": "November"}},"updated_at": {"time24": "16:13","time": "04:13","date": "2018-11-28","datetime": "2018-11-28 16:13:21","meta": {"meridiem": "pm","day": 28,"month": 11,"year": 2018,"month_name": "November"}},"relationships": {"permissions": [{"name": "admin","label": "Admin"}]}}}}}
生成的PHP根类:
class Response { public Data $data; public function getData(): Data { return $this->data; } public function setData(Data $data): self { $this->data = $data; return $this; } }
执行反序列化代码:
/** @var Response $response */ $response = $this->serializer->deserialize($response->getContent(), Response::class, 'json');
触发错误:
Failed to denormalize attribute "data" value for class "App\Http\Booking\Models\Response": Expected argument of type "App\Http\Booking\Models\Data", "array" given at property path "data".
自定义Serializer实例代码:
class MySerializer { public static function getSerializer() { $encoders = [new JsonEncoder()]; $normalizers = [new ObjectNormalizer(), new ArrayDenormalizer()]; return new Serializer($normalizers, $encoders); } }
报错原因
默认ObjectNormalizer无法自动识别PHP属性的类型声明(如public Data $data),导致将JSON中data字段解析为普通数组,而非Data类对象,与属性类型要求冲突。
解决方案
方案1:添加属性信息提取器
通过引入PropertyInfoExtractor,让ObjectNormalizer读取PHP类型声明信息,自动将数组转换为对应类对象。
- 安装依赖组件(未安装时执行):
composer require symfony/property-info
- 修改自定义Serializer代码:
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\PropertyInfo\PropertyInfoExtractor; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; class MySerializer { public static function getSerializer() { $encoders = [new JsonEncoder()]; // 创建支持PHP类型声明和PHPDoc的属性信息提取器 $phpDocExtractor = new PhpDocExtractor(); $reflectionExtractor = new ReflectionExtractor(); $propertyInfoExtractor = new PropertyInfoExtractor( [], [$phpDocExtractor, $reflectionExtractor] ); $normalizers = [ new ObjectNormalizer(null, null, null, $propertyInfoExtractor), new ArrayDenormalizer() ]; return new Serializer($normalizers, $encoders); } }
方案2:使用Serializer注解明确类型
通过给PHP类属性添加注解,指定属性对应的类类型,让ObjectNormalizer按注解解析。
- 安装依赖组件(未安装时执行):
composer require symfony/serializer-pack
- 修改Response类添加注解:
use Symfony\Component\Serializer\Annotation\Type; use Symfony\Component\Serializer\Annotation\SerializedName; class Response { #[Type(Data::class)] #[SerializedName('data')] public Data $data; public function getData(): Data { return $this->data; } public function setData(Data $data): self { $this->data = $data; return $this; } }
- 修改自定义Serializer启用注解加载:
use Doctrine\Common\Annotations\AnnotationReader; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; class MySerializer { public static function getSerializer() { $encoders = [new JsonEncoder()]; // 初始化注解元数据加载器 $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); $normalizers = [ new ObjectNormalizer($classMetadataFactory), new ArrayDenormalizer() ]; return new Serializer($normalizers, $encoders); } }
方案选择
- 方案1适合已使用PHP类型声明的场景,无需额外编写注解,一次配置全局生效。
- 方案2适合需要自定义属性映射(如JSON键与PHP属性名不一致)或复杂类型定义的场景,灵活性更高。
内容的提问来源于stack exchange,提问作者H3lltronik
相关产品推荐
相关产品推荐

