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?
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?
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.