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

如何通过序列化/反序列化器在存入DB前修改值?自定义Denormalizer未生效

解决Symfony自定义Denormalizer不生效的问题

你的自定义PasswordNormalizer未触发,核心问题集中在三个方面:

  • getSupportedTypes方法配置错误,导致Symfony序列化组件无法识别它支持User::class类型
  • 未处理OBJECT_TO_POPULATE传入已有对象的场景
  • 链式Denormalizer的注入方式不符合组件要求

具体修复步骤

1. 修正getSupportedTypes方法

这个方法是Symfony Serializer判断归一化器支持类型的核心依据,你当前返回['*' => false]相当于告知组件该归一化器不支持任何类型,自然不会被调用。修改为:

public function getSupportedTypes(?string $format): array
{
    return [
        User::class => true,
    ];
}

2. 适配OBJECT_TO_POPULATE场景

当反序列化时传入已有对象(通过OBJECT_TO_POPULATE上下文参数),需要直接复用该对象而非重新创建,调整denormalize方法逻辑:

public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed
{
    if ($type === User::class && isset($data['password'])) {
        $plainPassword = $data['password'];
        unset($data['password']);
        
        // 优先使用传入的已有对象,无则创建新对象
        $user = $context[AbstractNormalizer::OBJECT_TO_POPULATE] ?? $this->decorated->denormalize($data, $type, $format, $context);
        
        $hashedPassword = $this->passwordHasher->hashPassword($user, $plainPassword);
        $user->setPassword($hashedPassword);
        
        return $user;
    }

    return $this->decorated->denormalize($data, $type, $format, $context);
}

3. 调整链式Denormalizer注入逻辑

无需手动指定serializer.denormalizer.array,直接注入DenormalizerInterface即可,Symfony会自动处理归一化器链的委托逻辑。修改构造函数:

public function __construct(
    private readonly DenormalizerInterface $decorated,
    private UserPasswordHasherInterface $passwordHasher
) {
}

如果手动配置services.yaml,确保给服务加上serializer.normalizer标签:

services:
    App\Serializer\PasswordNormalizer:
        tags: [serializer.normalizer]

4. 验证反序列化调用代码

确保正确引入AbstractNormalizer常量:

use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;

$serializer->deserialize(
    $request->getContent(), 
    User::class,
    'json', 
    [ AbstractNormalizer::OBJECT_TO_POPULATE => $user]
);

完整修正后的PasswordNormalizer代码

<?php

namespace App\Serializer;

use App\Entity\User;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;

class PasswordNormalizer implements DenormalizerInterface
{
    public function __construct(
        private readonly DenormalizerInterface $decorated,
        private UserPasswordHasherInterface $passwordHasher
    ) {
    }

    public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed
    {
        if ($type === User::class && isset($data['password'])) {
            $plainPassword = $data['password'];
            unset($data['password']);
            
            // 优先使用传入的已有对象
            $user = $context[AbstractNormalizer::OBJECT_TO_POPULATE] ?? $this->decorated->denormalize($data, $type, $format, $context);
            
            $hashedPassword = $this->passwordHasher->hashPassword($user, $plainPassword);
            $user->setPassword($hashedPassword);
            
            return $user;
        }

        return $this->decorated->denormalize($data, $type, $format, $context);
    }

    public function supportsDenormalization($data, string $type, ?string $format = null, array $context = []): bool
    {
        return $type === User::class;
    }

    public function getSupportedTypes(?string $format): array
    {
        return [
            User::class => true,
        ];
    }
}

内容的提问来源于stack exchange,提问作者DevMoutarde

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 03:58:16