Я работаю над проектом Symfony 2 с версией 2.8 и использую встроенный компонент Serializer -> http://symfony.com/doc/current/components/serializer.html
У меня есть структура JSON, предоставляемая веб-службой.
После десериализации я хочу денормализовать мой контент в объектах. Вот моя структура (модель / марка в контексте автомобильного приложения).
[{
"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
объект, который содержит ссылку на 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());