如何用Symfony Serializer基于属性序列化/反序列化含对象属性的对象?
基于Symfony Serializer实现类属性级别的序列化与反序列化
要实现仅基于类属性而非get/set/is方法的序列化、反序列化(包含嵌套对象的场景),可以通过正确配置PropertyNormalizer并配合类型解析、对象构造支持来解决。以下是具体方案:
核心配置思路
PropertyNormalizer本身支持直接操作类属性,但默认不会自动处理嵌套对象的类型转换。需要开启类型属性支持、配置对象构造器,并配合EnumNormalizer处理枚举类型,就能解决反序列化时的类型错误问题。
步骤1:确保依赖安装
确保项目已安装以下Symfony组件:
composer require symfony/serializer symfony/property-access symfony/validator
symfony/validator用于读取PHPDoc注解中的类型信息(如果不用PHP原生类型属性的话)。
步骤2:配置Serializer服务
手动创建Serializer实例时,需要将PropertyNormalizer、EnumNormalizer(处理枚举)和JsonEncoder组合,并给PropertyNormalizer设置关键选项:
use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Normalizer\PropertyNormalizer; use Symfony\Component\Serializer\Normalizer\EnumNormalizer; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\ReflectionObjectConstructor; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\PropertyInfo\PropertyInfoExtractor; // 创建类型信息提取器:结合PHPDoc和反射获取属性类型 $phpDocExtractor = new PhpDocExtractor(); $reflectionExtractor = new ReflectionExtractor(); $propertyInfoExtractor = new PropertyInfoExtractor( [], [$phpDocExtractor, $reflectionExtractor] ); // 配置PropertyNormalizer:启用类型属性支持,指定对象构造器 $propertyNormalizer = new PropertyNormalizer( null, null, null, $propertyInfoExtractor, new ReflectionObjectConstructor() ); $propertyNormalizer->setDefaultContext([ PropertyNormalizer::ENABLE_TYPED_PROPERTIES => true, // 启用类型属性解析 PropertyNormalizer::ALLOW_EXTRA_ATTRIBUTES => false, // 禁止额外属性 ]); // 组合Serializer $serializer = new Serializer( [$propertyNormalizer, new EnumNormalizer()], [new JsonEncoder()] );
步骤3:序列化与反序列化示例
序列化
直接调用serialize方法,只会序列化类属性,不会包含getFullName()、getType()等业务方法的返回值:
// 创建测试对象 $workAddress = new Address(); $workAddress->setCity('LA'); $workAddress->setStreet('Central'); $workAddress->setAddressType(AddressTypeEnum::Work()); $homeAddress = new Address(); $homeAddress->setCity('Washington'); $homeAddress->setStreet('Some street'); $homeAddress->setAddressType(AddressTypeEnum::HOME()); $user = new User(); $user->setName('Tom'); $user->setSurname('Tailor'); $user->setAddresses([$workAddress, $homeAddress]); // 序列化 $json = $serializer->serialize(['user' => $user], 'json', [ 'json_encode_options' => JSON_PRETTY_PRINT ]); echo $json;
输出结果将和你期望的一致。
反序列化
调用deserialize方法,指定目标类型,Serializer会自动将嵌套数组转换为对应的对象:
$jsonData = '{ "user": { "name": "Tom", "surname": "Tailor", "addresses": [ { "addressType": "Work", "city": "LA", "street": "Central" }, { "addressType": "Home", "city": "Washington", "street": "Some street" } ] } }'; // 反序列化到数组,再提取User对象 $data = $serializer->deserialize($jsonData, 'array', 'json'); $user = $serializer->denormalize($data['user'], User::class); // 验证结果 var_dump($user->getName()); // 输出 "Tom" var_dump($user->getAddresses()[0] instanceof Address); // 输出 bool(true) var_dump($user->getAddresses()[0]->getAddressType()->getValue()); // 输出 "Work"
关键说明
- 类型信息的重要性:确保类属性有明确的PHP类型声明,或者通过PHPDoc注解(比如
/** @var Address[] */)标注类型,这样PropertyNormalizer才能正确识别嵌套对象的类型,完成自动转换。 - ReflectionObjectConstructor:该构造器通过反射直接实例化对象,不需要依赖setter方法,确保反序列化时能直接给私有属性赋值。
- EnumNormalizer:专门处理枚举类型的序列化/反序列化,将枚举值转换为字符串,反序列化时再还原为枚举实例。
内容的提问来源于stack exchange,提问作者Wiktor
相关产品推荐
相关产品推荐

