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

╄→гoц情女王★ 提交于 2019-12-07 04:24:56

问题


I'm trying to write a REST API which consumes a JSON from a PUT request in Symfony2. Deserializing the JSON to an entity sort of works – but the JMS Serializer seems to coerce types from the JSON instead of throwing an exception if the type of a property in the JSON does not match the entity’s corresponding property.

For example …

{ "id" : "123" }

… will result in …

int(123)

… if the property id is defined as an integer in the entity.

But I would like JMS Serializer to throw an exception instead. Does anyone know how to achieve this?

Update 2016-02-27

One problem with JMS Serializer’s type handling I found is this:

{ "id" : "n123" }

will result in …

int(0)

which is totally undesired.

Can someone please point me into the right direction?


回答1:


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.




回答2:


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



来源:https://stackoverflow.com/questions/35502472/how-to-make-jms-serializer-throw-an-exception-on-deserializing-json-instead-of-c

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