Компонент Serializer¶
Компонент Serializer предназначается для того, чтобы превращать объекты в определённый формат (XML, JSON, YAML, ...) и наоборот.
Для того, чтобы сделать это, компонент Serializer следует такой простой схеме.

Как вы можете увидеть на изображении выше, массив используются в качестве посредника между объектами и сериализованным содержанием. Таким образом, кодировщики (Encoders) будут работать только с превращением конкретных форматов в массивы и наоборот. Таким же образом, нормализаторы (Normalizers) будут работать с превращением определённых объектов в массивы и наоборот.
Сериализация - это сложная тема. Этот компонент может не охватить все ваши случаи применения, но может быть полезным для разработки инструментов для сериализации и десериализации ваших объектов.
Установка¶
1 | $ composer require symfony/serializer
|
Alternatively, you can clone the https://github.com/symfony/serializer repository.
Note
If you install this component outside of a Symfony application, you must
require the vendor/autoload.php
file in your code to enable the class
autoloading mechanism provided by Composer. Read
this article for more details.
Для использования ObjectNormalizer
, должен быть также установлен
PropertyAccess component.
Использование¶
Использовать компонент Сериализация очень просто. Вам просто нужно установить
Serializer
, указывая, какие кодировщики
и нормализатор будут доступны:
1 2 3 4 5 6 7 8 9 | use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);
|
Предпочитаемый нормализатор - ObjectNormalizer
,
но другие нормализаторы также доступны. Все примеры, показанные ниже, используют
ObjectNormalizer
.
Сериализация объекта¶
Ради этого примера, предположите, что следующий класс уже существует в AppModelпроекте:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | namespace App\Model;
class Person
{
private $age;
private $name;
private $sportsperson;
private $createdAt;
// Геттеры
public function getName()
{
return $this->name;
}
public function getAge()
{
return $this->age;
}
// Иссеры
public function isSportsperson()
{
return $this->sportsperson;
}
// Сеттеры
public function setName($name)
{
$this->name = $name;
}
public function setAge($age)
{
$this->age = $age;
}
public function setSportsperson($sportsperson)
{
$this->sportsperson = $sportsperson;
}
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
}
}
|
Теперь, если вы хотите сериализовать этот объект в JSON, вам просто нужно использовать сервис Serializer, созданный ранее:
1 2 3 4 5 6 7 8 9 10 | $person = new App\Model\Person();
$person->setName('foo');
$person->setAge(99);
$person->setSportsperson(false);
$jsonContent = $serializer->serialize($person, 'json');
// $jsonContent содержит {"name":"foo","age":99,"sportsperson":false,"createdAt":null}
echo $jsonContent; // или вернуть его в Ответе
|
Первый параметр serialize()
-
это объект, который должен быть сериализован, а второй - используются для выбора
правильного кодировщика, в этом случае -
JsonEncoder
.
Десериализация объекта¶
Теперь вы узнаете, как делать с точностью до наоборот. В этот раз, информация
класса Person
будет зашифрована в формате XML:
1 2 3 4 5 6 7 8 9 10 11 | use App\Model\Person;
$data = <<<EOF
<person>
<name>foo</name>
<age>99</age>
<sportsperson>false</sportsperson>
</person>
EOF;
$person = $serializer->deserialize($data, Person::class, 'xml');
|
В этом случае, deserialize()
требует трёх параметров:
- Информацию, которую нужно расшифровать
- Имя класса, в который будет расшифрована эта информация
- Кодировщик, используемый для преобразования этой информации в массив
По умолчанию, дополнительные атрибуты, которые не связываются с денормализованным
объектом, будут проигнорированы компонентом Сереализатор. Установите ключ
allow_extra_attributes
контекста десериализации, как false
, чтобы позволить
сериализатору вызывать исключение, когда передаются дополнительные атрибуты:
1 2 3 4 5 6 7 8 9 10 11 12 13 | $data = <<<EOF
<person>
<name>foo</name>
<age>99</age>
<city>Paris</city>
</person>
EOF;
// это вызовет Symfony\Component\Serializer\Exception\ExtraAttributesException
// так как "город" не являтся атрибутом класса Особа
$person = $serializer->deserialize($data, 'App\Model\Person', 'xml', array(
'allow_extra_attributes' => false,
));
|
Десериализация в существующем объекте¶
Serializer также может быть использован для обновления существующего объекта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // ...
$person = new Person();
$person->setName('bar');
$person->setAge(99);
$person->setSportsperson(true);
$data = <<<EOF
<person>
<name>foo</name>
<age>69</age>
</person>
EOF;
$serializer->deserialize($data, Person::class, 'xml', array('object_to_populate' => $person));
// $person = App\Model\Person(name: 'foo', age: '69', sportsperson: true)
|
Это распространённая необходимость, при работе с ORM.
Группы атрибутов¶
Иногда вам захочется сериализовать разные наборы атрибутов из ваших сущностей. Группы являются удобным способом достижения этого.
Предположите, что у вас есть следующий простой PHP объект:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | namespace App\Model;
class MyObj
{
public $foo;
private $bar;
public function getBar()
{
return $this->bar;
}
public function setBar($bar)
{
return $this->bar = $bar;
}
}
|
Определение сериализатора может быть указано используя аннотация, XML или YAML.
ClassMetadataFactory
,
который будет использован нормализатором должен знать, какой формат использовать.
Инициализируйте ClassMetadataFactory
,
как показано далее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
// Для аннотаций
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
// Для XML
// используйте Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader;
// Для YAML
// используйте Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader;
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
// Для XML
// $classMetadataFactory = new ClassMetadataFactory(new XmlFileLoader('/path/to/your/definition.xml'));
// Для YAML
// $classMetadataFactory = new ClassMetadataFactory(new YamlFileLoader('/path/to/your/definition.yaml'));
|
Далее, создайте ваше определение групп:
- Annotations
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
namespace App\Model; use Symfony\Component\Serializer\Annotation\Groups; class MyObj { /** * @Groups({"group1", "group2"}) */ public $foo; /** * @Groups({"group3"}) */ public function getBar() // is* methods are also supported { return $this->bar; } // ... }
- YAML
1 2 3 4 5 6
App\Model\MyObj: attributes: foo: groups: ['group1', 'group2'] bar: groups: ['group3']
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<?xml version="1.0" ?> <serializer xmlns="http://symfony.com/schema/dic/serializer-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/serializer-mapping http://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd" > <class name="App\Model\MyObj"> <attribute name="foo"> <group>group1</group> <group>group2</group> </attribute> <attribute name="bar"> <group>group3</group> </attribute> </class> </serializer>
Теперь вы можете сериализовать атрибуты только в тех группах, в которых вы хотите:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
$obj = new MyObj();
$obj->foo = 'foo';
$obj->setBar('bar');
$normalizer = new ObjectNormalizer($classMetadataFactory);
$serializer = new Serializer(array($normalizer));
$data = $serializer->normalize($obj, null, array('groups' => array('group1')));
// $data = array('foo' => 'foo');
$obj2 = $serializer->denormalize(
array('foo' => 'foo', 'bar' => 'bar'),
'MyObj',
null,
array('groups' => array('group1', 'group3'))
);
// $obj2 = MyObj(foo: 'foo', bar: 'bar')
|
Note
In order to use the annotation loader, you should have installed the
doctrine/annotations
and doctrine/cache
packages with Composer.
Tip
Annotation classes aren't loaded automatically, so you must load them using a class loader like this:
1 2 3 4 5 6 7 8 9 | use Composer\Autoload\ClassLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;
/** @var ClassLoader $loader */
$loader = require __DIR__.'/../vendor/autoload.php';
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
return $loader;
|
Выбор определённых атрибутов¶
Также возможно сериализовать только набор определённых атрибутов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
class User
{
public $familyName;
public $givenName;
public $company;
}
class Company
{
public $name;
public $address;
}
$company = new Company();
$company->name = 'Les-Tilleuls.coop';
$company->address = 'Lille, France';
$user = new User();
$user->familyName = 'Dunglas';
$user->givenName = 'Kévin';
$user->company = $company;
$serializer = new Serializer(array(new ObjectNormalizer()));
$data = $serializer->normalize($user, null, array('attributes' => array('familyName', 'company' => ['name'])));
// $data = array('familyName' => 'Dunglas', 'company' => array('name' => 'Les-Tilleuls.coop'));
|
Доступны только атрибуты, которые не игнорируются (см. ниже). Если установлены какие-то группы сериализации, то могут быть использованы только атрибуты, разрешённые этими группами.
Что касается групп, атрибуты могут быть выбраны как во время процесса сериализации, так и десериализации.
Игнорирование атрибутов¶
Note
Использование группы атрибутов вместо метода
setIgnoredAttributes()
,
считантся хорошей практикой.
Как вариант, существует способ игнорирования атрибутов из исходного объекта. Чтобы
удалить эти атрибуты, используйте метод
setIgnoredAttributes()
в определении нормализатора:
1 2 3 4 5 6 7 8 9 10 | use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
$normalizer = new ObjectNormalizer();
$normalizer->setIgnoredAttributes(array('age'));
$encoder = new JsonEncoder();
$serializer = new Serializer(array($normalizer), array($encoder));
$serializer->serialize($person, 'json'); // Output: {"name":"foo","sportsperson":false}
|
Преобразование имён свойств при сериализации и десериализации¶
Иногда сериализованные атрибуты должны быть названы отлично от свойств или методов геттера / сеттера PHP классов.
Компонент Serializer предоставляет удобный способ для перевода илил соединения имён PHP поля с сериализованными именами: Система преобразования имён.
Предполагая, что у вас есть следующий объект:
1 2 3 4 5 | class Company
{
public $name;
public $address;
}
|
И в сериализованной форме, все атрибуты должны иметь префикс org_
,
как показано далее:
1 | {"org_name": "Acme Inc.", "org_address": "123 Main Street, Big City"}
|
Пользовательский преобразователь имён может обработать такие случаи:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
class OrgPrefixNameConverter implements NameConverterInterface
{
public function normalize($propertyName)
{
return 'org_'.$propertyName;
}
public function denormalize($propertyName)
{
// удалить префикс 'org_'
return 'org_' === substr($propertyName, 0, 4) ? substr($propertyName, 4) : $propertyName;
}
}
|
Пользовательский нормализатор может быть использован, путём передачи его в качестве
второго аргумента любого класса, расширяющего AbstractNormalizer
,
включая GetSetMethodNormalizer
и PropertyNormalizer
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
$nameConverter = new OrgPrefixNameConverter();
$normalizer = new ObjectNormalizer(null, $nameConverter);
$serializer = new Serializer(array($normalizer), array(new JsonEncoder()));
$company = new Company();
$company->name = 'Acme Inc.';
$company->address = '123 Main Street, Big City';
$json = $serializer->serialize($company, 'json');
// {"org_name": "Acme Inc.", "org_address": "123 Main Street, Big City"}
$companyCopy = $serializer->deserialize($json, Company::class, 'json');
// Те же данные, что и $company
|
Из CamelCase в snake_case¶
Во многих форматах распространено использование нижних подчёркиваний для разделения слов (также известно, как snake_case). Однако, в приложениях Symfony часто используется CamelCase для именования свойств (несмотря на то, что стандарт PSR-1 не рекомендует никакой определённый стиль для имён свойств).
Symfony предоставляет встроенный преобразователь имён, созданный для преобразований между стилями snake_case и CamelCased во время процессов сериализации и десериализации:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
$normalizer = new ObjectNormalizer(null, new CamelCaseToSnakeCaseNameConverter());
class Person
{
private $firstName;
public function __construct($firstName)
{
$this->firstName = $firstName;
}
public function getFirstName()
{
return $this->firstName;
}
}
$kevin = new Person('Kévin');
$normalizer->normalize($kevin);
// ['first_name' => 'Kévin'];
$anne = $normalizer->denormalize(array('first_name' => 'Anne'), 'Person');
// Объект Person с firstName: 'Anne'
|
Сериализация булевых атрибутов¶
Если вы используете методы иссеров (методы, с префиксом is
, вроде
Acme\Person::isSportsperson()
), компонент Serializer автоматически
определит его и использует для сериализации связанных атрибутов.
ObjectNormalizer
также заботится о методах, начинающихся на has
, add
и remove
.
Использование обратных вызовов для сериализации свойств с экземплярами объектов¶
При сериализации вы можете устаналивать обратный вызов для форматирования определённого свойства объекта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | use Acme\Person;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Serializer;
$encoder = new JsonEncoder();
$normalizer = new GetSetMethodNormalizer();
$callback = function ($dateTime) {
return $dateTime instanceof \DateTime
? $dateTime->format(\DateTime::ISO8601)
: '';
};
$normalizer->setCallbacks(array('createdAt' => $callback));
$serializer = new Serializer(array($normalizer), array($encoder));
$person = new Person();
$person->setName('cordoval');
$person->setAge(34);
$person->setCreatedAt(new \DateTime('now'));
$serializer->serialize($person, 'json');
// Вывод: {"name":"cordoval", "age": 34, "createdAt": "2014-03-22T09:43:12-0500"}
|
Нормализаторы¶
Существует несколько доступных типов сериализаторов:
ObjectNormalizer
Этот нормализатор использует компонент PropertyAccess, чтобы читать и писать в объекте. Это означает, что он имеет доступ к свойствам напрямую и через геттеры, сеттеры, хасссеры, дополнители и удалители. Он поддерживает вызов конструктора во время процесса денормализации.
Объекты нормализируются в карту имён свойств и значений (имена генерируются путём удаления префикса``get``,
set
,has
илиremove
из имени метода и преобразования первой буквы в нижний регистр; например,getFirstName()
->firstName
).ObjectNormalizer
- это наиболее мощный нормализатор. Он конфигурируется по умолчанию при использовании стандартной версии Symfony со включённым сериализатором.GetSetMethodNormalizer
Этот нормализатор читает содержание класса, вызывая "геттеры" (публичные методы, начинающиеся на "get"). Он денормализует данные, вызвав конструктор и "сеттеры" (публичные методы, начинающиеся на "set").
Объекты нормализируются в карту имён свойств и значений (имена генерируются путём удаления префикса``get`` из имени метода и преобразования первой буквы в нижний регистр; например,
getFirstName()
->firstName
).PropertyNormalizer
Этот нормализатор напрямую читает и пишет публичные свойства, а также приватные и защищённые свойства (как из класса, так и из всех его родительских классов). Он поддерживает вызов конструктора во время процесса денормализации.
Объекты нормализируются в карту имён свойств значений свойства.
JsonSerializableNormalizer
Этот нормализатор работает с классами, реализующими
JsonSerializable
.Он вызовет метод
JsonSerializable::jsonSerialize()
, а потом ещё больше нормализует результат. Это означает, что встроенные классыJsonSerializable
также будут нормализованы.Этот нормализатор особенно полезен, когда вы хотите постепенно перейти с существующей базы кода, используя простую
json_encode
, на Serializer Symfony, который позволит вам смешивать используемые нормализаторы для разных классов.В отличие от
json_encode
, могут обрабатываться циклические ссылки.DateTimeNormalizer
- Этот нормализатор преобразует объекты
DateTimeInterface
(например,DateTime
иDateTimeImmutable
) в строки. По умолчанию, он использует формат RFC3339. DataUriNormalizer
- Этот нормализатор преобразует объекты
SplFileInfo
в строку данных URI (data:...
), чтобы файлы могли быть встроены в сериализованные данные. DateIntervalNormalizer
- Этот нормализатор преобразует объекты
DateInterval
в строки. По умолчанию он использует форматP%yY%mM%dDT%hH%iM%sS
. ConstraintViolationListNormalizer
Этот нормализатор преобразует объекты, которые реализуют
ConstraintViolationListInterface
в список ошибок согласно стандарту RFC 7807.New in version 4.1:
ConstraintViolationListNormalizer
появился в Symfony 4.1.
Кодировщики¶
Кодировщики превращают массивы в форматы и наоборот. Они реализуют
EncoderInterface
для кодирования (массив в формат) и
DecoderInterface
для декодирования
(формат в массив).
Вы можете добавить новые кодировщики в экземпляр Serializer используя второй аргумент конструктора:
1 2 3 4 5 6 | use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
$encoders = array(new XmlEncoder(), new JsonEncoder());
$serializer = new Serializer(array(), $encoders);
|
Встроенные кодировщики¶
Компонент Serializer предоставляет несколько встроенных кодировщиков:
JsonEncoder
- Этот класс зашифровывает и расшифровывает данные в JSON.
XmlEncoder
- Этот класс зашифровывает и расшифровывает данные в XML.
YamlEncoder
- Этот кодировщик зашифровывает и расшифровывает данные в YAML. Кодировщик требует компонент Yaml.
CsvEncoder
- Этот кодировщик зашифровывает и расшифровывает данные в CSV.
Все эти кодировщики включены по умолчанию при использовании стандартной версии Symfony с включенным сериализатором.
Кодировщик JsonEncoder
¶
JsonEncoder
кодирует и декодирует в и из строк JSON основываясь на функциях PHP
json_encode
и json_decode
.
Кодировщик CsvEncoder
¶
CsvEncoder
кодирует и декодирует в и из CSV.
Вы можете передать ключ контекста as_collection
для того, чтобы результаты
всегда были коллекцией.
New in version 4.1: Опция as_collection
появилась в Symfony 4.1.
Кодировщик XmlEncoder
¶
Этот кодировщик преобразует массивы в XML и наоборот.
Например, возьмём объект нормализированный следующим образом:
1 | array('foo' => array(1, 2), 'bar' => true);
|
XmlEncoder
закодирует этот объект так:
1 2 3 4 5 6 | <?xml version="1.0"?>
<response>
<foo>1</foo>
<foo>2</foo>
<bar>1</bar>
</response>
|
Учтите, что этот кодировщик понимает ключи, начинающиеся на @
как аттрибуты:
1 2 3 4 5 6 7 | $encoder = new XmlEncoder();
$encoder->encode(array('foo' => array('@bar' => 'value')));
// вернёт:
// <?xml version="1.0"?>
// <response>
// <foo bar="value" />
// </response>
|
Вы можете передать ключ контекста as_collection
для того, чтобы результаты
всегда были коллекцией.
New in version 4.1: Опция as_collection
появилась в Symfony 4.1.
Tip
XML комментарии по умолчанию игнорируются при декодировании содержимого, но это
поведение может быть изменено опциональным аргументом $ignoredNodeTypes
конструктора класса XmlEncoder
.
New in version 4.1: XML комментарии игнорируются по умолчанию с Symfony 4.1.
Кодировщик YamlEncoder
¶
Этот кодировщик требует Yaml Component и преобразует в и из Yaml.
Работа с циклическими ссылками¶
Циклические ссылки распространены при работе с отношениями сущности:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | class Organization
{
private $name;
private $members;
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function setMembers(array $members)
{
$this->members = $members;
}
public function getMembers()
{
return $this->members;
}
}
class Member
{
private $name;
private $organization;
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function setOrganization(Organization $organization)
{
$this->organization = $organization;
}
public function getOrganization()
{
return $this->organization;
}
}
|
Чтобы избежать бесконечных циклов,
GetSetMethodNormalizer
или
ObjectNormalizer
, вызоваите
CircularReferenceException
,
когда столкнётесь с таким случаем:
1 2 3 4 5 6 7 8 9 10 | $member = new Member();
$member->setName('Kévin');
$organization = new Organization();
$organization->setName('Les-Tilleuls.coop');
$organization->setMembers(array($member));
$member->setOrganization($organization);
echo $serializer->serialize($organization, 'json'); // Вызывает CircularReferenceException
|
Метод setCircularReferenceLimit()
этого нормализатора устаналивает количество раз,
которое он будет сериализовать один и тот же объект, до признания его цикличной ссылкой.
Его значение по умолчанию - 1
.
Вместо вызова исключения, цикличные ссылки также могут быть обработаны пользовательскими вызываемыми. Это особенно полезно при сериализации сущностей с уникальными идентификаторами:
1 2 3 4 5 6 7 8 9 10 | $encoder = new JsonEncoder();
$normalizer = new ObjectNormalizer();
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getName();
});
$serializer = new Serializer(array($normalizer), array($encoder));
var_dump($serializer->serialize($org, 'json'));
// {"name":"Les-Tilleuls.coop","members":[{"name":"K\u00e9vin", organization: "Les-Tilleuls.coop"}]}
|
Работа с глубиной сериализации¶
Компонент Сериализация может определять и ограничивать глубину сериализации. Это особенно полезно при сериализации больших древ. Представьте следующую структуру данных:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | namespace Acme;
class MyObj
{
public $foo;
/**
* @var self
*/
public $child;
}
$level1 = new MyObj();
$level1->foo = 'level1';
$level2 = new MyObj();
$level2->foo = 'level2';
$level1->child = $level2;
$level3 = new MyObj();
$level3->foo = 'level3';
$level2->child = $level3;
|
Serializer может быть сконфигурирован, чтобы установить максимальную глубину
данного свойства. Здесь мы установили его, как 2 для свойства $child
:
- Annotations
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\Serializer\Annotation\MaxDepth; namespace Acme; class MyObj { /** * @MaxDepth(2) */ public $child; // ... }
- YAML
1 2 3 4
Acme\MyObj: attributes: child: max_depth: 2
- XML
1 2 3 4 5 6 7 8 9 10
<?xml version="1.0" ?> <serializer xmlns="http://symfony.com/schema/dic/serializer-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/serializer-mapping http://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd" > <class name="Acme\MyObj"> <attribute name="child" max-depth="2" /> </class> </serializer>
Загрузчик меаданных, соответствующий выбранному формату, должен быть сконфигурирован для того, чтобы использовать эту функцию. Это делается автоматически при использовании стандартной версии Symfony. При использовани компонента отдельно, см. документацию групп, чтобы узнать, как это сделать.
Проверка производится только если ключ``enable_max_depth`` контекста сериализатора
установлен, как true
. В следующем примере, третий уровень не сериализуется, так
как он глубже, чем максимальная сконфигурированная глубина (2):
1 2 3 4 5 6 7 8 9 10 11 12 | $result = $serializer->normalize($level1, null, array('enable_max_depth' => true));
/*
$result = array(
'foo' => 'level1',
'child' => array(
'foo' => 'level2',
'child' => array(
'child' => null,
),
),
);
*/
|
Вместо выбрасывания исключения, может быть выполнена пользовательская функция при достижении максимальной глубины. Это особенно полезно при сериализации сущностей с уникальными идентификаторами:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Annotation\MaxDepth;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
class Foo
{
public $id;
/**
* @MaxDepth(1)
*/
public $child;
}
$level1 = new Foo();
$level1->id = 1;
$level2 = new Foo();
$level2->id = 2;
$level1->child = $level2;
$level3 = new Foo();
$level3->id = 3;
$level2->child = $level3;
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$normalizer = new ObjectNormalizer($classMetadataFactory);
$normalizer->setMaxDepthHandler(function ($foo) {
return '/foos/'.$foo->id;
});
$serializer = new Serializer(array($normalizer));
$result = $serializer->normalize($level1, null, array(ObjectNormalizer::ENABLE_MAX_DEPTH => true));
/*
$result = array(
'id' => 1,
'child' => array(
'id' => 2,
'child' => '/foos/3',
),
);
*/
|
New in version 4.1: Метод setMaxDepthHandler()
появился в Symfony 4.1.
Раббота с массивами¶
Компонент Serializer способен также работать с массивами объектов. Сериализация массивов работает так же, как и сериализация одного объекта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | use Acme\Person;
$person1 = new Person();
$person1->setName('foo');
$person1->setAge(99);
$person1->setSportsperson(false);
$person2 = new Person();
$person2->setName('bar');
$person2->setAge(33);
$person2->setSportsperson(true);
$persons = array($person1, $person2);
$data = $serializer->serialize($persons, 'json');
// $data содержит [{"name":"foo","age":99,"sportsperson":false},{"name":"bar","age":33,"sportsperson":true}]
|
Если вы хотите десериализовать такую структуру, то вам нужно добавить
ArrayDenormalizer
к набору нормализаторов. Добавив []
к типу параметра метода
deserialize()
, вы
обозначите, что вы ожидаете массив вместо одного объекта.
1 2 3 4 5 6 7 8 9 10 11 12 | use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Serializer;
$serializer = new Serializer(
array(new GetSetMethodNormalizer(), new ArrayDenormalizer()),
array(new JsonEncoder())
);
$data = ...; // Сериализованные данные из предыдщуего примера
$persons = $serializer->deserialize($data, 'Acme\Person[]', 'json');
|
Кодировщик XmlEncoder
¶
Этот кодировщик преобразует массивы в XML и наоборот. Например, возьмите объект, нормализованный таким образом:
1 | array('foo' => array(1, 2), 'bar' => true);
|
XmlEncoder
зашифровывает этот объект следующим образом:
1 2 3 4 5 6 | <?xml version="1.0"?>
<response>
<foo>1</foo>
<foo>2</foo>
<bar>1</bar>
</response>
|
Ключи массива, начинающиеся с @
, считаются XML атрибутами:
1 2 3 4 5 6 7 | array('foo' => array('@bar' => 'value'));
// зашифровывается таким образом:
// <?xml version="1.0"?>
// <response>
// <foo bar="value" />
// </response>
|
Используйте специальный ключ #
, чтобы определить данные узла:
1 2 3 4 5 6 7 8 9 | array('foo' => array('@bar' => 'value', '#' => 'baz'));
// зашифровывается таким образом:
// <?xml version="1.0"?>
// <response>
// <foo bar="value">
// baz
// </foo>
// </response>
|
Контекст¶
Метод encode()
определяет третий необязательный параметр под названием context
,
который определяет опции конфигурации для ассоциативного массива XmlEncoder:
1 | $xmlEncoder->encode($array, 'xml', $context);
|
Вот доступные опции:
xml_format_output
- Если установлена, как true, форматирует сгенерированный XML с разрывами строчек и отступами.
xml_version
- Устанавливает атрибут версии XML (по умолчанию:
1.1
). xml_encoding
- Устанавливает атрибут шифроваия XML (по умолчанию:
utf-8
). xml_standalone
- Добавляет отдельный атрибут в сгенерированном XML (по умолчанию:
true
). xml_root_node_name
- Устанавливает имя корневого узла (по умолчанию:
response
). remove_empty_tags
- Если установлена, как true, удаляет все пустые теги в сгенерированном XML.
Обработка аргументов конструктора¶
New in version 4.1: Опция default_constructor_arguments
появилась в Symfony 4.1.
Если у конструктора класса есть аргументы, что обычно случается в
Value Objects, сериализатор не сможет создать объект, если пропущены какие-то
аргументы. В данных случаях используйте контекстную опцию default_constructor_arguments
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
class MyObj
{
private $foo;
private $bar;
public function __construct($foo, $bar)
{
$this->foo = $foo;
$this->bar = $bar;
}
}
$normalizer = new ObjectNormalizer($classMetadataFactory);
$serializer = new Serializer(array($normalizer));
$data = $serializer->denormalize(
array('foo' => 'Hello'),
'MyObj',
array('default_constructor_arguments' => array(
'MyObj' => array('foo' => '', 'bar' => ''),
)
));
// $data = new MyObj('Hello', '');
|
Рекурсивная денормализация и безопасность типа¶
Компонент Serializer может использовать PropertyInfo Component, чтобы денормализовать сложные типы (объекты). Тип свойства класса будет предположен, используя предоставленный извлекатель и использован для рекурсивной денормализации внутренних данных.
При использовании стандартной версии Symfony, все нормализаторы автоматически
конфигурируются, чтобы использовать зарегистрированные извлекатели. При использовании
копомнента самостоятельно, реализация
PropertyTypeExtractorInterface
(обычно экземпляр
PropertyInfoExtractor
) должна быть передана в
качестве 4го параметра ObjectNormalizer
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
namespace Acme;
class ObjectOuter
{
private $inner;
private $date;
public function getInner()
{
return $this->inner;
}
public function setInner(ObjectInner $inner)
{
$this->inner = $inner;
}
public function setDate(\DateTimeInterface $date)
{
$this->date = $date;
}
public function getDate()
{
return $this->date;
}
}
class ObjectInner
{
public $foo;
public $bar;
}
$normalizer = new ObjectNormalizer(null, null, null, new ReflectionExtractor());
$serializer = new Serializer(array(new DateTimeNormalizer(), $normalizer));
$obj = $serializer->denormalize(
array('inner' => array('foo' => 'foo', 'bar' => 'bar'), 'date' => '1988/01/21'),
'Acme\ObjectOuter'
);
dump($obj->getInner()->foo); // 'foo'
dump($obj->getInner()->bar); // 'bar'
dump($obj->getDate()->format('Y-m-d')); // '1988-01-21'
|
Если доступен PropertyTypeExtractor
, то нормализатор также проверит, чтобы
данные для денормализации соответствовали типу свойства (даже для примитивных
типов). Например, если предоставлена string
, но тип свойства - int
, будет
вызвано UnexpectedValueException
.
Принуждение типа свойства можно отключить, установив опцию контекста сериализатора
ObjectNormalizer::DISABLE_TYPE_ENFORCEMENT
, как true
.
Сериализация интерфейсов и абстрактных классов¶
При работе с объектами, которые достаточно похожи или имеют общие свойства, вы можете использовать интерфейсы или абстрактные классы. Компонент Serializer позволяет вам сериализовать и десериализовать эти объекты, используя "отображение класса дискриминатора"
Дискриминатор - это поле (в сериализованной строке), используемое для дифференциации
между возможными объектами. На практике, при использвании компонента Serializer,
передайте реализацию ClassDiscriminatorResolverInterface
ObjectNormalizer
.
Рассмотрите приложение, которое определяет абстрактный класс CodeRepository
,
расширенный классами GitHubCodeRepository
и BitBucketCodeRepository
. Этот
пример показывает, как сериализовать и десериализовать эти объекты:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // ...
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
$discriminator->addClassMapping(CodeRepository::class, new ClassDiscriminatorMapping('type', [
'github' => GitHubCodeRepository::class,
'bitbucket' => BitBucketCodeRepository::class,
]));
$serializer = new Serializer(
array(new ObjectNormalizer($classMetadataFactory, null, null, null, $discriminator)),
array('json' => new JsonEncoder())
);
$serialized = $serializer->serialize(new GitHubCodeRepository());
// {"type": "github"}
$repository = $serializer->unserialize($serialized, CodeRepository::class, 'json');
// экземпляр GitHubCodeRepository
|
Если фабрика метаданных класса включена, как объясняется в разделе Группы атрибутов, вы можете использовать эту упрощённую конфигурацию:
- Annotations
1 2 3 4 5 6 7 8 9 10 11 12 13 14
namespace App; use Symfony\Component\Serializer\Annotation\DiscriminatorMap; /** * @DiscriminatorMap(typeProperty="type", mapping={ * "github"="App\GitHubCodeRepository", * "bitbucket"="App\BitBucketCodeRepository" * }) */ interface CodeRepository { // ... }
- YAML
1 2 3 4 5 6
App\CodeRepository: discriminator_map: type_property: type mapping: github: 'App\GitHubCodeRepository' bitbucket: 'App\BitBucketCodeRepository'
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13
<?xml version="1.0" ?> <serializer xmlns="http://symfony.com/schema/dic/serializer-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/serializer-mapping http://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd" > <class name="App\CodeRepository"> <discriminator-map type-property="type"> <mapping type="github" class="App\GitHubCodeRepository" /> <mapping type="bitbucket" class="App\BitBucketCodeRepository" /> </discriminator-map> </class> </serializer>
Узнать больше¶
v1.12.0
были выпущены под
лицензией Apache, поэтому несовместимэ с проектами GPLv2).Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.