Как заставить JMS Serializer выдавать исключение при десериализации JSON вместо принуждения типов?

Я пытаюсь написать REST API, который использует JSON из запроса PUT в Symfony2. Десериализация JSON в сущность вроде как работает, но сериализатор JMS, кажется, принуждает типы из JSON вместо того, чтобы выдавать исключение, если тип свойства в JSON не соответствует соответствующему свойству сущности.

Например …

{ "id" : "123" }

… приведет к…

int(123)

… если свойство id определено как целое число в объекте.

Но я бы хотел, чтобы JMS Serializer вместо этого выдавал исключение. Кто-нибудь знает, как этого добиться?

Обновление 2016-02-27

Одна проблема с обработкой типов JMS Serializer, которую я обнаружил, заключается в следующем:

{ "id" : "n123" }

приведет к…

int(0)

что совершенно нежелательно.

Может ли кто-нибудь указать мне правильное направление?


person reieRMeister    schedule 19.02.2016    source источник


Ответы (2)


После получения помощи на Github я хочу поделиться ответом на свой вопрос.

Ключом к решению является использование пользовательского обработчика, который реализует JMS\Serializer\Handler\SubscribingHandlerInterface (например, 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);
    }
}

Затем вам нужно будет определить сериализатор как службу:

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

Тогда вы сможете использовать тип strict_integer:

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

После этого десериализация в контроллере работает как обычно.

Бонус: использование валидатора типов теперь, наконец, имеет смысл:

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

Я надеюсь, что это поможет тем, у кого такая же проблема.

person reieRMeister    schedule 20.04.2016

Основываясь на ответе reieRMeister, когда дело доходит до десериализации JSON, я не думаю, что разумно принуждать примитивные типы по умолчанию.

Ответ reieRMaster хорош, когда необходимо провести различие между «строгим» и «свободным» типом, явно установив тип strict_integer для каждого свойства. Но это становится несколько утомительным, если вы хотите использовать встроенную функцию, в которой тип определяется с помощью метаданных доктрины.

Вы можете переопределить это поведение по умолчанию для всех примитивных типов, переопределив jms_serializer.json_deserialization_visitor.class одним из ваших собственных классов, как показано ниже:

<?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;
    }

}

и в services.xml (или services.yml) переопределить jms_serializer.json_deserialization_visitor.class

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

Попался! Убедитесь, что ваш пакет зарегистрирован ПОСЛЕ пакета JMSSerializer, иначе описанное выше не будет работать. Как это сделать описано здесь

person dnshio    schedule 30.06.2016
comment
Спасибо за то, что поделился этим! Я посмотрю на это. - person reieRMeister; 04.07.2016