Symfony Serializer反序列化时如何预处理Base64编码属性?
解决Symfony反序列化时自动Base64解码属性的方法
针对你遇到的需求,这里提供三种可行的实现方式,均基于Symfony Serializer组件的扩展能力:
方法一:自定义属性转换器(精准单属性处理)
这种方式只针对Foo类的bar属性做解码处理,是最精准的方案,适合单个属性的定制需求。
- 实现
PropertyConverterInterface创建专属转换器
use Symfony\Component\Serializer\Converter\PropertyConverterInterface; use Symfony\Component\Serializer\Exception\InvalidArgumentException; final class Base64PropertyConverter implements PropertyConverterInterface { public function convert(mixed $value, string $propertyName, string $className, array $context): mixed { // 仅处理Foo类的bar属性 if ($className !== Foo::class || $propertyName !== 'bar') { return $value; } if (!is_string($value)) { throw new InvalidArgumentException('Base64编码值必须为字符串类型'); } $decoded = base64_decode($value, true); if ($decoded === false) { throw new InvalidArgumentException('无效的Base64字符串'); } return $decoded; } public function supports(string $propertyName, string $className, array $context): bool { return $className === Foo::class && $propertyName === 'bar'; } }
- 修改Serializer实例,注入转换器
return new Serializer( [ new ArrayDenormalizer(), new ObjectNormalizer( propertyTypeExtractor: new PropertyInfoExtractor( typeExtractors: [new ReflectionExtractor()] ), propertyConverters: [new Base64PropertyConverter()] // 新增属性转换器 ) ], [new JsonEncoder()] );
反序列化后,$object->bar将自动变为解码后的Hello world。
方法二:自定义Foo类专属反序列化器
如果需要对Foo类的整体反序列化流程做定制,可创建仅处理该类的Denormalizer:
- 实现
ContextAwareDenormalizerInterface和SerializerAwareInterface
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\SerializerAwareInterface; use Symfony\Component\Serializer\SerializerInterface; final class FooDenormalizer implements ContextAwareDenormalizerInterface, SerializerAwareInterface { private ObjectNormalizer $objectNormalizer; public function __construct(ObjectNormalizer $objectNormalizer) { $this->objectNormalizer = $objectNormalizer; } public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed { // 提前解码bar字段 if (isset($data['bar']) && is_string($data['bar'])) { $decoded = base64_decode($data['bar'], true); if ($decoded !== false) { $data['bar'] = $decoded; } } // 交给原生ObjectNormalizer完成后续反序列化 return $this->objectNormalizer->denormalize($data, $type, $format, $context); } public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool { return $type === Foo::class; } public function setSerializer(SerializerInterface $serializer): void { $this->objectNormalizer->setSerializer($serializer); } }
- 调整Serializer实例的Denormalizer顺序(自定义Denormalizer需放在ObjectNormalizer之前)
$objectNormalizer = new ObjectNormalizer( propertyTypeExtractor: new PropertyInfoExtractor( typeExtractors: [new ReflectionExtractor()] ) ); return new Serializer( [ new ArrayDenormalizer(), new FooDenormalizer($objectNormalizer), // 自定义反序列化器优先 $objectNormalizer ], [new JsonEncoder()] );
方法三:注解+通用上下文处理器(多属性场景友好)
如果需要灵活标记多个属性进行Base64解码,可结合注解和通用处理器实现:
- 给
Foo类的属性添加上下文标记
use Symfony\Component\Serializer\Annotation\Context; final class Foo { // 标记该属性需要Base64解码 #[Context(['base64_decode' => true])] public string $bar; }
- 创建通用的Base64解码处理器
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\SerializerAwareInterface; use Symfony\Component\Serializer\SerializerInterface; final class Base64Denormalizer implements ContextAwareDenormalizerInterface, SerializerAwareInterface { private ObjectNormalizer $objectNormalizer; public function __construct(ObjectNormalizer $objectNormalizer) { $this->objectNormalizer = $objectNormalizer; } public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed { // 获取属性元数据,检查是否需要解码 $propertyMetadata = $this->objectNormalizer->getPropertyMetadataFactory()?->create($type); if ($propertyMetadata) { foreach ($propertyMetadata as $propertyName => $metadata) { $propertyContext = $metadata->getContext() ?? []; if (isset($propertyContext['base64_decode']) && $propertyContext['base64_decode'] && isset($data[$propertyName])) { $decoded = base64_decode($data[$propertyName], true); if ($decoded !== false) { $data[$propertyName] = $decoded; } } } } return $this->objectNormalizer->denormalize($data, $type, $format, $context); } public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool { // 仅支持带有base64_decode标记的类 $propertyMetadata = $this->objectNormalizer->getPropertyMetadataFactory()?->create($type); if (!$propertyMetadata) { return false; } foreach ($propertyMetadata as $metadata) { $propertyContext = $metadata->getContext() ?? []; if (isset($propertyContext['base64_decode']) && $propertyContext['base64_decode']) { return true; } } return false; } public function setSerializer(SerializerInterface $serializer): void { $this->objectNormalizer->setSerializer($serializer); } }
- 注册处理器到Serializer实例(同样需放在ObjectNormalizer之前)
$objectNormalizer = new ObjectNormalizer( propertyTypeExtractor: new PropertyInfoExtractor( typeExtractors: [new ReflectionExtractor()] ) ); return new Serializer( [ new ArrayDenormalizer(), new Base64Denormalizer($objectNormalizer), $objectNormalizer ], [new JsonEncoder()] );
内容的提问来源于stack exchange,提问作者Spomky-Labs
相关产品推荐
相关产品推荐

