Компонент Сериализатор

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

Для того, чтобы сделать это, компонент Сериалищатор следуюет такой простой схеме.

../_images/serializer_workflow.png

Как вы можете увидеть на картинке выше, массив используются в качестве посредника. Таким образом, Кодировщики будут работать только с превращением конкретных форматов в массивы и наоборот. Таким же образом, Нормализаторы будут работать с превращением определённых объектов в массивы и наоборот.

Сериализация - это сложная тема, и хотя компонент может работать не во всех случаях, он может быть полезным инструментом при разработке инструментов для сериализации и десериализации ваших объектов.

Установка

Вы можете установить компонент 2 разными способами:

Then, require the vendor/autoload.php file to enable the autoloading mechanism provided by Composer. Otherwise, your application won't be able to find the classes of this Symfony component.

Для использования 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.

Сериализация объекта

Ради этого примера, предположите, что следующий класс уже существует в вашем проекте:

 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
namespace Acme;

class Person
{
    private $age;
    private $name;
    private $sportsman;

    // Геттеры
    public function getName()
    {
        return $this->name;
    }

    public function getAge()
    {
        return $this->age;
    }

    // Иссеры
    public function isSportsman()
    {
        return $this->sportsman;
    }

    // Сеттеры
    public function setName($name)
    {
        $this->name = $name;
    }

    public function setAge($age)
    {
        $this->age = $age;
    }

    public function setSportsman($sportsman)
    {
        $this->sportsman = $sportsman;
    }
}

Теперь, если вы хотите сериализовать этот объект в JSON, вам просто нужно использовать сервис Сериализатор, созданный ранее:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$person = new Acme\Person();
$person->setName('foo');
$person->setAge(99);
$person->setSportsman(false);

$jsonContent = $serializer->serialize($person, 'json');

// $jsonContent содержит {"name":"foo","age":99,"sportsman":false}

echo $jsonContent; // или вернуть его в Ответе

Первый параметр serialize() - это объект, который должен быть сериализован, а второй - используются для выбора правильного кодировщика, в этом случае - JsonEncoder.

Десериализация объекта

Теперь вы узнаете, как делать с точностью до наоборот. В этот раз, информация класса Person будет зашифрована в формате XML:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
use Acme\Person;

$data = <<<EOF
<person>
    <name>foo</name>
    <age>99</age>
    <sportsman>false</sportsman>
</person>
EOF;

$person = $serializer->deserialize($data, Person::class, 'xml');

В этом случае, deserialize() требует трёх параметров:

  1. Информацию, которую нужно расшифровать
  2. Имя класса, в который будет расшифрована эта информация
  3. Кодировщик, используемый для преобразования этой информации в массив

New in version 3.3: Поддержка ключа allow_extra_attributes в контексте была представлена в Symfony 3.3.

По умолчанию, дополнительные атрибуты, которые не связываются с денормализованным объектом, будут проигнорированы компонентом Сереализатор. Установите ключ 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, 'Acme\Person', 'xml', array(
    'allow_extra_attributes' => false,
));

Десериализация в существующем объекте

Сериализатор также может быть использован для обновления существующего объекта:

 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->setSportsman(true);

$data = <<<EOF
<person>
    <name>foo</name>
    <age>69</age>
</person>
EOF;

$serializer->deserialize($data, Person::class, 'xml', array('object_to_populate' => $person));
// $person = Acme\Person(name: 'foo', age: '69', sportsman: true)

Это распространённая необходимость, при работе с ORM.

Группы атрибутов

Иногда вам захочется сериализовать разные наборы атрибутов из ваших сущностей. Группы являются удобным способом достижения этого.

Предположите, что у вас есть следующий простой PHP объект:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
namespace Acme;

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

Далее, создайте ваше определение групп:

  • Annotations
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    namespace Acme;
    
    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
    Acme\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="Acme\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

Использование группы атрибутов вместо метода 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","sportsman":false}

Преобразование имён свойств при сериализации и десериализации

Иногда сериализованные атрибуты должны быть названы отлично от свойств или методов геттера / сеттера PHP классов.

Rомпонент Сериализатор предоставляет удобный способ для перевода илил соединения имён 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()));

$obj = new Company();
$obj->name = 'Acme Inc.';
$obj->address = '123 Main Street, Big City';

$json = $serializer->serialize($obj);
// {"org_name": "Acme Inc.", "org_address": "123 Main Street, Big City"}
$objCopy = $serializer->deserialize($json);
// Те же данные, что и $obj

Из CamelCase в snake_case

Во многих форматах распространено использование нижних подчёркиваний для разделения слов (также известно, как snake_case). Однако, PSR-1 указывает, что предпочитаемый стиль свойств и методов PHP - CamelCase.

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::isSportsman()), компонент Сериализатор автоматически определит его и использует для сериализации связанных атрибутов.

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" и преобразованное в нижний регистр) значений свойства.

ObjectNormalizer - это наиболее мощный нормализатор. Он конфигурируется по умолчанию при использовании стандартной версии Symfony со включённым сериализатором.

GetSetMethodNormalizer

Этот нормализатор читает содержание класса, вызывая "геттеры" (публичные методы, начинающиеся на "get"). Он денормализует данные, вызвав конструктор и "сеттеры" (публичные методы, начинающиеся на "set").

Объекты нормализируются в карту имён свойств (имя метода без префикса "get" и преобразованное в нижний регистр) значений свойства.

PropertyNormalizer

Этот нормализатор напрямую читает и пишет публичные свойства, а также приватные и защищённые свойства. Он поддерживает вызов конструктора во время процесса денормализации.

Объекты нормализируются в карту имён свойств значений свойства.

JsonSerializableNormalizer

Этот нормализатор работает с классами, реализующими JsonSerializable.

Он вызовет метод JsonSerializable::jsonSerialize(), а потом ещё больше нормализует результат. Это означает, что встроенные классы JsonSerializable также будут нормализованы.

Этот нормализатор особенно полезен, когда вы хотите постепенно перейти с существующей базы кода, используя простую json_encode, на Сериализатор Symfony, который позволит вам смешивать используемые нормализаторы для разных классов.

В отличие от json_encode, могут обрабатываться циклические ссылки.

DateTimeNormalizer

Этот нормализатор преобразует объекты DateTimeInterface (например, DateTime и DateTimeImmutable) в строки. По умолчанию, он использует формат RFC3339.

New in version 3.2: Поддержка указания формата даты и времени во время денормализации была представлена в DateTimeNormalizer в Symfony 3.2.

DataUriNormalizer
Этот нормализатор преобразует объекты SplFileInfo в строку данных URI (data:...), чтобы файлы могли быть встроены в сериализованные данные.

Кодировщики

Компонент Сериализатор поддерживает множество форматов сразу после установки:

JsonEncoder
Этот класс зашифровывает и расшифровывает данные в JSON.
XmlEncoder
Этот класс зашифровывает и расшифровывает данные в XML.
YamlEncoder
Этот кодировщик зашифровывает и расшифровывает данные в YAML. Кодировщик требует компонент Yaml.
CsvEncoder
Этот кодировщик зашифровывает и расшифровывает данные в CSV.

Все эти кодировщики включены по умолчанию при использовании стандартной версии Symfony с включенным сериализатором.

New in version 3.2: Кодировщики YamlEncoder и CsvEncoder были представлены в Symfony 3.2

Работа с циклическими ссылками

Циклические ссылки распространены при работе с отношениями сущности:

 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 вызывает CircularReferenceException, когда сталкивается с таким случаем:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$member = new Member();
$member->setName('Kévin');

$org = new Organization();
$org->setName('Les-Tilleuls.coop');
$org->setMembers(array($member));

$member->setOrganization($org);

echo $serializer->serialize($org, '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;

Сериализатор может быть сконфигурирован, чтобы установить максимальную глубину данного свойства. Здесь мы установили его, как 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 $foo;
    
        // ...
    }
    
  • YAML
    1
    2
    3
    4
    Acme\MyObj:
        attributes:
            foo:
                max_depth: 2
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    <?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="foo">
                <max-depth>2</max-depth>
            </attribute>
    </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
use Acme\Person;

$person1 = new Person();
$person1->setName('foo');
$person1->setAge(99);
$person1->setSportsman(false);

$person2 = new Person();
$person2->setName('bar');
$person2->setAge(33);
$person2->setSportsman(true);

$persons = array($person1, $person2);
$data = $serializer->serialize($persons, 'json');

// $data содержит [{"name":"foo","age":99,"sportsman":false},{"name":"bar","age":33,"sportsman":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');

Рекурсивная денормализация и безопасность типа

Компонент Сериализатор может использовать 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.

Узнать больше

Популярной альтернативой компоненту Сериализатор Symfony является сторонняя библиотека - JMS сериализатор (выпущенный под лицензией Apache, поэтому несовместимый с проектами GPLv2).

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.