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

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类型声明信息,自动将数组转换为对应类对象。

  1. 安装依赖组件(未安装时执行):
composer require symfony/property-info
  1. 修改自定义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按注解解析。

  1. 安装依赖组件(未安装时执行):
composer require symfony/serializer-pack
  1. 修改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;
    }
}
  1. 修改自定义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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 02:15:26