Denormalize nested structure in objects with Symfony 2 serializer Denormalize nested structure in objects with Symfony 2 serializer symfony symfony

Denormalize nested structure in objects with Symfony 2 serializer


The ObjectNormalizer needs more configuration. You will at least need to supply the fourth parameter of type PropertyTypeExtractorInterface.

Here's a (rather hacky) example:

<?phpuse Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;use Symfony\Component\PropertyInfo\Type;use Symfony\Component\Serializer\Encoder\JsonEncoder;use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;use Symfony\Component\Serializer\Serializer;$a = new VehicleModel();$a->id = 0;$a->code = 1;$a->model = 'modalA';$a->make = new VehicleMake();$a->make->id = 0;$a->make->code = 1;$a->make->name = 'makeA';$b = new VehicleModel();$b->id = 1;$b->code = 2;$b->model = 'modelB';$b->make = new VehicleMake();$b->make->id = 0;$b->make->code = 1;$b->make->name = 'makeA';$data = [$a, $b];$serializer = new Serializer(    [new ObjectNormalizer(null, null, null, new class implements PropertyTypeExtractorInterface {        /**         * {@inheritdoc}         */        public function getTypes($class, $property, array $context = array())        {            if (!is_a($class, VehicleModel::class, true)) {                return null;            }            if ('make' !== $property) {                return null;            }            return [                new Type(Type::BUILTIN_TYPE_OBJECT, true, VehicleMake::class)            ];        }    }), new ArrayDenormalizer()],    [new JsonEncoder()]);$json = $serializer->serialize($data, 'json');print_r($json);$models = $serializer->deserialize($json, VehicleModel::class . '[]', 'json');print_r($models);

Note that in your example json, the first entry has an array as value for make. I took this to be a typo, if it's deliberate, please leave a comment.

To make this more automatic you might want to experiment with the PhpDocExtractor.


In cases when you need more flexibility in denormalization it's good to create your own denormalizers.

$serializer = new Serializer(  [    new ArrayNormalizer(),     new VehicleDenormalizer(),     new VehicleMakeDenormalizer()  ], [    new JsonEncoder()  ]);$models = $serializer->deserialize(  $data,   '\Namespace\VehicleModel[]',   'json');

Here the rough code of such denormalizer

class VehicleDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface    {      public function denormalize($data, $class, $format, $context)       {        $vehicle = new VehicleModel();        ...        $vehicleMake = $this->denormalizer->denormalize(          $data->make,          VehicleMake::class,          $format,          $context        );        $vehicle->setMake($vehicleMake);        ...      }    }

I only have doubts on should we rely on $this->denormalizer->denormalize (which works properly just because we use Symfony\Component\Serializer\Serializer) or we must explicitly inject VehicleMakeDenormalizer into VehicleDenormalizer

$vehicleDenormalizer = new VehicleDenormalizer();$vehicleDenormalizer->setVehicleMakeDenormalizer(new VehicleMakeDenormalizer());


The easiest way would be to use the ReflectionExtractor if your Vehicle class has some type hints.

class VehicleModel {    public $id;    public $code;    public $model;    /** @var VehicleMake */    public $make;}

You can pass the Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor as argument to the ObjectNormalizer when you initialize the Serializer

$serializer = new Serializer([new ObjectNormalizer(null, null, null, new ReflectionExtractor()), new ArrayDenormalizer()], [new JsonEncoder()]);$models = $serializer->deserialize($data, '\Namespace\VehicleModel[]', 'json');