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');