Я работаю над проектом Symfony 2 с версией 2.8, и я использую встроенный компонент Serializer -> http://symfony.com/doc/current/components/serializer.html
У меня есть структура JSON, предоставляемая веб-службой. После десериализации я хочу денормализовать свой контент в объектах. Вот моя структура (модель / make в контексте использования автомобиля).
[{ "0": { "id": 0, "code": 1, "model": "modelA", "make": { "id": 0, "code": 1, "name": "makeA" } } } , { "1": { "id": 1, "code": 2, "model": "modelB", "make": { "id": 0, "code": 1, "name": "makeA" } } }]
Моя идея – заполнить объект VehicleModel
в VehicleModel
содержится ссылка на объект VehicleMake
.
class VehicleModel { public $id; public $code; public $model; public $make; // VehicleMake }
Вот что я делаю:
// Retrieve data in JSON $data = ... $serializer = new Serializer([new ObjectNormalizer(), new ArrayDenormalizer()], [new JsonEncoder()]); $models = $serializer->deserialize($data, '\Namespace\VehicleModel[]', 'json');
В результате мой объект VehicleModel
правильно заполнен, но $make
является логически массивом ключей / значений. Здесь я хочу вместо этого VehicleMake
.
Есть ли способ сделать это ?
Спасибо
ObjectNormalizer
нуждается в дополнительной настройке. Вам, по крайней мере, нужно будет предоставить четвертый параметр типа PropertyTypeExtractorInterface
.
Вот пример (довольно взломанный):
<?php use 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);
Обратите внимание, что в вашем примере json первая запись имеет массив как значение для make
. Я взял это, чтобы быть опечаткой, если это преднамеренно, пожалуйста, оставьте комментарий.
Чтобы сделать это более автоматическим, вы можете поэкспериментировать с PhpDocExtractor
.
В случаях, когда вам нужна большая гибкость в денормализации, полезно создавать собственные денормализаторы.
$serializer = new Serializer( [ new ArrayNormalizer(), new VehicleDenormalizer(), new VehicleMakeDenormalizer() ], [ new JsonEncoder() ] ); $models = $serializer->deserialize( $data, '\Namespace\VehicleModel[]', 'json' );
Здесь грубый код такого денормализатора
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); ... } }
У меня есть только сомнения, следует ли нам полагаться на $this->denormalizer->denormalize
(который работает правильно только потому, что мы используем Symfony\Component\Serializer\Serializer
) или мы должны явно вводить VehicleMakeDenormalizer
в VehicleDenormalizer
$vehicleDenormalizer = new VehicleDenormalizer(); $vehicleDenormalizer->setVehicleMakeDenormalizer(new VehicleMakeDenormalizer());