使用Symfony序列化程序组件

问题描述

我是Symfony序列化程序组件的新手。我正在尝试将JSON正文正确反序列化为以下DTO:

class PostDTO
{
    /** @var string */
    private $name;

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName(string $name): void
    {
        $this->name = $name;
    }
}

控制器方法如下:

/**
 * @Route (path="",methods={"POST"},name="new_post")
 * @param Request $request
 * @return Response
 */
public function create(Request $request): Response
{
    $model = $this->serializer->deserialize($request->getContent(),PostDTO::class,'json');
    // call the service with the model
    return new JsonResponse();
}

我的问题是,身体反序列化后,我想进行业务验证。但是,如果我为名称指定了无效的值,例如false[],则反序列化将失败,并出现以下异常:Symfony\Component\Serializer\Exception\NotnormalizableValueException: "The type of the "name" attribute for class "App\Service\PostDTO" must be one of "string" ("array" given).

我知道这是因为我故意设置了"name": []。但是,我一直在寻找一种将字段设置为认值甚至进行反序列化之前的验证的方法

解决方法

我找到了解决此问题的正确方法。之所以抛出该异常,是因为序列化程序无法使用我提供的无效有效负载创建PostDTO类。

为了解决这个问题,我创建了自定义的反规范化器,该反规范化器仅针对该特定类启动。为此,我像这样实现了DenormalizerInterface

use App\Service\PostDTO;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;

class PostDTODeserializer implements DenormalizerInterface
{
    /** @var ObjectNormalizer */
    private $normalizer;

    /**
     * PostDTODeserializer constructor.
     * @param ObjectNormalizer $normalizer
     */
    public function __construct(ObjectNormalizer $normalizer)
    {
        $this->normalizer = $normalizer;
    }

    public function denormalize($data,string $type,string $format = null,array $context = [])
    {
        return $type === PostDTO::class;
    }

    /**
     * @param mixed $data
     * @param string $type
     * @param string|null $format
     * @return array|bool|object
     * @throws ExceptionInterface
     */
    public function supportsDenormalization($data,string $format = null)
    {
        // validate the array which will be normalized (you should write your validator and inject it through the constructor)
        if (!is_string($data['name'])) {
            // normally you would throw an exception and leverage the `ErrorController` functionality

            // do something
        }

        // convert the array to the object
        return $this->normalizer->denormalize($data,$type,$format);
    }
}

如果要访问context数组,则可以实现DenormalizerAwareInterface。通常,您将创建自定义验证,并将其注入此反规范化器并验证$data数组。

请不要在这里注入ObjectNormalizer,以便当数据成功通过验证时,我仍然可以使用PostDTO来构造$data

PS:就我而言,自动装配已自动注册了我的自定义反规范化器。如果您的计算机没有自动自动接线,请转到services.yaml并添加以下几行:

 App\Serializer\PostDTODeserializer:
        tags: ['serializer.normalizer']

(我用serializer.normalizer标记了实现,以便在反序列化管道中识别出该实现)