Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony: How to make JMS Serializer works with strict types?

This is my situation:

I'm trying to write a Symfony REST API that works with "strict" types (integer, boolean and float), because default Symfony behaviour doesn't support it and I want to avoid force cast types (e.g: JMS Serializer converts string value into integer field type)

To do this, I have created a custom handler which implements the JMS\Serializer\Handler\SubscribingHandlerInterface (e.g a StrictIntegerHandler):

<?php

namespace AppBundle\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);
    }
}

My entity looks:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Validator;

/**
 * Person
 *
 * @ORM\Table(name="persons")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\PersonRepository")
 */
class Person
{
    /**
     * @var int age
     *
     * @ORM\Column(name="age", type="integer")
     *
     * @Serializer\Type("strict_integer")
     * @Serializer\Groups({"Person"})
     *
     * @Validator\Type(type="integer", message="Age field has wrong type")
     */
    private $age;

    public function getAge()
    {
        return $this->age;
    }

    public function setAge(int $age)
    {
        $this->age = $age;
    }
}

When I throw the following POST actions, the JMS Serializer returns the correct results:

  1. { "age" : 12 } will result in int(12)
  2. { "age" : "asdf" } will result in "Age field has wrong type"

In both cases my method deserializeStrictIntegerFromJSON is called, so deserialize process works perfectly as I want.

Problems comes with serialize process: When I launch GET action (/person/id_person) I get the following exception:

Expected object but got integer. Do you have the wrong @Type mapping or could this be a Doctrine many-to-many relation? (JMS\Serializer\Exception\LogicException)

Debug stack trace shows me that method serializeStrictIntegerToJSON is never called..

How can I solve it? Thanks.

like image 885
Wildchild Avatar asked Nov 07 '22 22:11

Wildchild


1 Answers

I have found the solution: I had to upgrade jms/serializer library to version 1.5.0.

My problem was that I was using jms/serializer (v1.1.0) whose SerializationContext class was throwing the previous LogicException in isVisiting() method because strict_integer type is not recognized in switch-case sentence from accept() method of GraphNavigator class.

like image 121
Wildchild Avatar answered Nov 14 '22 22:11

Wildchild