Symfony-docs: [Serializer] Add information about how nested object can be deserialized with PropertyTypeExtractor

Created on 18 Jan 2017  路  5Comments  路  Source: symfony/symfony-docs

If you follow the Serializer documentation http://symfony.com/doc/current/components/serializer.html) and/or "how to use the serializer component" documentation (https://symfony.com/doc/current/serializer.html), and you try to deserialize/denormalize an object with an "object" attribute or an "array of objects" attribute, you end with an array of values instead of an instance of the nested object.

The solution is easy: using the PropertyInfo component and their PropertyTypeExtractors. And easier if you use the symfony framework and PHPDoc blocks: you only have to enable the PropertyInfo component in the framework configuration.

But nothing of these is mentioned in both documents (not even a mention of this PropertyInfo component configuration in the Framework Bundle configuration reference http://symfony.com/doc/current/reference/configuration/framework.html)

Serializer

Most helpful comment

Spent a lot of time today researching the problem.
The solution is found. But, I hope there is a simpler solution.

Env:
php 5
symfony 3.4

My case:

Role {
   /**
    * @var int
    */
   private $id;

   public function getId() {}
   public function setId() {}
}
User {
   /**
    * @var Role
    */
   private $role;

   public function geRole() {}
   public function setRole($role) {}
}



md5-d2bed96773cecd42d198727e02e37670



{
  "id": 1,
  "role": {
    "id": 3
  }
}



md5-ac06d4640283fc6347c7f5c857d75696



namespace AppBundle\Serializer\PropertyInfo\Extractor;

use AppBundle\Model\User;
use AppBundle\Model\Role;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\PropertyInfo\Type;

/**
 * CustomModelExtractor.
 */
class CustomModelExtractor implements PropertyTypeExtractorInterface
{
    /**
     * @var ReflectionExtractor
     */
    private $reflectionExtractor;

    /**
     * CustomModelExtractor constructor.
     */
    public function __construct()
    {
        $this->reflectionExtractor = new ReflectionExtractor();
    }

    /**
     * @param string $class
     * @param string $property
     * @param array  $context
     *
     * @return array|Type[]|null
     */
    public function getTypes($class, $property, array $context = array())
    {
        if (\is_a($class, User::class, true) && 'role' === $property) {
            return [
                new Type(Type::BUILTIN_TYPE_OBJECT, true, Role::class)
            ];
        }

        return $this->reflectionExtractor->getTypes($class, $property, $context);
    }
}



md5-d46c2e4933d1c557ede4f2329303cb92



    app.security.property_info.extractor.custom_model:
        class: AppBundle\Serializer\PropertyInfo\Extractor\CustomModelExtractor
        tags:
            - { name: 'property_info.list_extractor' }
            - { name: 'property_info.type_extractor' }
            - { name: 'property_info.access_extractor' }

Hope this will save you time.

All 5 comments

Does this work with Symfony 2.8 though?

Hi. I was also looking for ability to deserialize to nested models. Not sure if the documentation describes this. I by chance found this which helped me, perhaps someone else will find this of use. https://stackoverflow.com/questions/42729405/decode-multiple-model-in-php-symfony-from-json-to-object

I lost a few hours on Friday finding out the same thing as you, @magarzon. I'm happy to try to work up a PR for this, though I'd appreciate some advice on where it should go.

I think the actual config snippet belongs on the "how to use the serializer component", and then a link should be added on the serializer component docs. What do you think?

Spent a lot of time today researching the problem.
The solution is found. But, I hope there is a simpler solution.

Env:
php 5
symfony 3.4

My case:

Role {
   /**
    * @var int
    */
   private $id;

   public function getId() {}
   public function setId() {}
}
User {
   /**
    * @var Role
    */
   private $role;

   public function geRole() {}
   public function setRole($role) {}
}



md5-d2bed96773cecd42d198727e02e37670



{
  "id": 1,
  "role": {
    "id": 3
  }
}



md5-ac06d4640283fc6347c7f5c857d75696



namespace AppBundle\Serializer\PropertyInfo\Extractor;

use AppBundle\Model\User;
use AppBundle\Model\Role;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\PropertyInfo\Type;

/**
 * CustomModelExtractor.
 */
class CustomModelExtractor implements PropertyTypeExtractorInterface
{
    /**
     * @var ReflectionExtractor
     */
    private $reflectionExtractor;

    /**
     * CustomModelExtractor constructor.
     */
    public function __construct()
    {
        $this->reflectionExtractor = new ReflectionExtractor();
    }

    /**
     * @param string $class
     * @param string $property
     * @param array  $context
     *
     * @return array|Type[]|null
     */
    public function getTypes($class, $property, array $context = array())
    {
        if (\is_a($class, User::class, true) && 'role' === $property) {
            return [
                new Type(Type::BUILTIN_TYPE_OBJECT, true, Role::class)
            ];
        }

        return $this->reflectionExtractor->getTypes($class, $property, $context);
    }
}



md5-d46c2e4933d1c557ede4f2329303cb92



    app.security.property_info.extractor.custom_model:
        class: AppBundle\Serializer\PropertyInfo\Extractor\CustomModelExtractor
        tags:
            - { name: 'property_info.list_extractor' }
            - { name: 'property_info.type_extractor' }
            - { name: 'property_info.access_extractor' }

Hope this will save you time.

Hope this may help others:

It's actually in the documentation.

Notice the part about the "4th" parameter. In my case I gave a new \Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor() as 4th argument.

Was this page helpful?
0 / 5 - 0 ratings