如何用Symfony Serializer将JSON反序列化为含实体数组的DTO
解决Symfony Serializer反序列化对象数组的问题
问题根源在于Symfony Serializer无法自动推断数组内的对象类型,必须显式指定数组元素的DTO类型,同时确保Serializer能读取到这些类型元数据。以下是具体实现步骤:
1. 为DTO属性添加类型元数据
根据你的PHP版本选择对应的方式:
方式一:PHP注解(兼容PHP 7+)
给ArrDTO的arr属性添加@Type注解,明确数组元素为SomeDTO:
namespace App\DTO; use Symfony\Component\Serializer\Annotation\Type; class SomeDTO { private int $id; private string $name; // 生成对应的getter和setter方法 public function getId(): int { return $this->id; } public function setId(int $id): void { $this->id = $id; } public function getName(): string { return $this->name; } public function setName(string $name): void { $this->name = $name; } } class ArrDTO { /** * @Type("array<App\DTO\SomeDTO>") */ private array $arr; // 生成对应的getter和setter方法 public function getArr(): array { return $this->arr; } public function setArr(array $arr): void { $this->arr = $arr; } }
方式二:PHP 8+属性
用PHP原生属性替代注解,写法更简洁:
namespace App\DTO; use Symfony\Component\Serializer\Annotation\Type; class SomeDTO { private int $id; private string $name; // getter和setter同上 } class ArrDTO { #[Type('array<App\DTO\SomeDTO>')] private array $arr; // getter和setter同上 }
方式三:PHP 8.1+泛型类型(推荐)
如果使用PHP 8.1及以上,可直接用泛型数组声明,配合PhpDoc提示:
namespace App\DTO; class SomeDTO { private int $id; private string $name; // getter和setter同上 } class ArrDTO { /** @var list<SomeDTO> */ private array $arr; // 或者直接用PHP 8.1泛型语法: // private array<SomeDTO> $arr; // getter和setter同上 }
2. 确保Serializer能读取类型元数据
独立使用Serializer(非Symfony框架)
需要手动初始化Serializer,加载注解/属性元数据:
use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\JsonEncoder; 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; // 加载注解元数据(如果用注解方式) $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); // 初始化Serializer $normalizers = [new ObjectNormalizer($classMetadataFactory)]; $encoders = [new JsonEncoder()]; $serializer = new Serializer($normalizers, $encoders); // 执行反序列化 $jsonData = '{"arr": [{"id":1,"name":"test1"},{"id":2,"name":"test2"}]}'; $arrDto = $serializer->deserialize($jsonData, ArrDTO::class, 'json'); // 此时$arrDto->getArr()返回的是SomeDTO对象数组
Symfony框架内使用
框架默认已配置好Serializer服务,只需确保安装了必要依赖:
- 若用注解:安装
doctrine/annotations - 若用PHP属性:无需额外依赖,直接注入
SerializerInterface即可
示例控制器代码:
namespace App\Controller; use App\DTO\ArrDTO; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Serializer\SerializerInterface; class TestController extends AbstractController { public function deserialize(Request $request, SerializerInterface $serializer) { $jsonData = $request->getContent(); $arrDto = $serializer->deserialize($jsonData, ArrDTO::class, 'json'); // 后续逻辑 } }
注意事项
- 确保DTO类的属性有对应的getter和setter方法,否则Serializer无法正常赋值(或配置ObjectNormalizer允许直接访问私有属性)
- 若使用泛型方式,需确保Serializer启用了PhpDoc元数据加载(Symfony框架默认已启用)
内容的提问来源于stack exchange,提问作者dfionov
相关产品推荐
相关产品推荐

