How to make JMS Serializer throw an exception on deserializing JSON instead of coercing types?

廉价感情. 提交于 2019-12-05 10:53:32

After getting help over at Github I want to share an answer on my own question.

The key to a solution is using a custom handler which implements the JMS\Serializer\Handler\SubscribingHandlerInterface (e.g. a StrictIntegerHandler).

<?php
namespace MyBundle\Serializer;

use JMS\Serializer\Context;
use JMS\Serializer\GraphNavigator;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\JsonDeserializationVisitor;
use JMS\Serializer\JsonSerializationVisitor;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class StrictIntegerHandler implements SubscribingHandlerInterface
{
    public static function getSubscribingMethods()
    {
        return [
            [
                'direction' => GraphNavigator::DIRECTION_DESERIALIZATION,
                'format' => 'json',
                'type' => 'strict_integer',
                'method' => 'deserializeStrictIntegerFromJSON',
            ],
            [
                'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
                'format' => 'json',
                'type' => 'strict_integer',
                'method' => 'serializeStrictIntegerToJSON',
            ],
        ];
    }

    public function deserializeStrictIntegerFromJSON(JsonDeserializationVisitor $visitor, $data, array $type)
    {
        return $data;
    }

    public function serializeStrictIntegerToJSON(JsonSerializationVisitor $visitor, $data, array $type, Context $context)
    {
        return $visitor->visitInteger($data, $type, $context);
    }
}

You will then need to define the serializer as a service:

services:
    mybundle.serializer.strictinteger:
        class: MyBundle\Serializer\StrictIntegerHandler
        tags:
            - { name: jms_serializer.subscribing_handler }

Then you will be able to use the type strict_integer:

MyBundle\Entity\MyEntity:
    exclusion_policy: ALL
    properties:
        id:
            expose: true
            type: strict_integer

Deserializing in the controller then works as usual.

Bonus: Using a type validator now finally makes sense:

MyBundle\Entity\MyEntity:
    properties:
        id:
            - Type:
                type: integer
                message: id {{ value }} is not an integer.

I hope this helps those with the same problem.

dnshio

Building on top of reieRMeister's answer, when it comes to JSON deserialisation, I don't think it's wise to coerce primitive types by default.

reieRMaster's answer is good when a distinction needs to be made between "strict" and "loose" type by explicitly setting the type to strict_integer for each property. But this becomes somewhat tedious if you wish to use the built in feature where the type is determined using doctrine metadata.

You can override this default behaviour for all the primitive types by overriding the jms_serializer.json_deserialization_visitor.class with one of your own classes like below:

<?php
namespace MyBundle\Serializer\Visitor;

use JMS\Serializer\JsonDeserializationVisitor;
use JMS\Serializer\Context;

class JsonNativeDeserializationVisitor extends JsonDeserializationVisitor
{

    public function visitString($data, array $type, Context $context)
    {
        return $data;
    }

    public function visitBoolean($data, array $type, Context $context)
    {
        return $data;
    }

    public function visitInteger($data, array $type, Context $context)
    {
        return $data;
    }

    public function visitDouble($data, array $type, Context $context)
    {
        return $data;
    }

}

and in services.xml (or services.yml) override jms_serializer.json_deserialization_visitor.class

<parameters>
    <parameter key="jms_serializer.json_deserialization_visitor.class">MyBundle\Serializer\Visitor\JsonNativeDeserializationVisitor</parameter>
</parameters>

Gotcha! Make sure that your bundle is registered AFTER JMSSerializer bundle, or the above will not work. How to do that is described here

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!