mirror of
https://github.com/symfony/symfony-docs.git
synced 2026-03-23 16:22:10 +01:00
See the symfony/serializer changelog https://github.com/symfony/serializer/blob/7.3/CHANGELOG.md#73 ``` 7.3 .... Add NumberNormalizer to normalize BcMath\Number and GMP as string ```
2596 lines
93 KiB
ReStructuredText
2596 lines
93 KiB
ReStructuredText
How to Use the Serializer
|
|
=========================
|
|
|
|
Symfony provides a serializer to transform data structures from one format
|
|
to PHP objects and the other way around.
|
|
|
|
This is most commonly used when building an API or communicating with third
|
|
party APIs. The serializer can transform an incoming JSON request payload
|
|
to a PHP object that is consumed by your application. Then, when generating
|
|
the response, you can use the serializer to transform the PHP objects back
|
|
to a JSON response.
|
|
|
|
It can also be used to, for instance, load CSV configuration data as PHP
|
|
objects, or even to transform between formats (e.g. YAML to XML).
|
|
|
|
.. _activating_the_serializer:
|
|
|
|
Installation
|
|
------------
|
|
|
|
In applications using :ref:`Symfony Flex <symfony-flex>`, run this command to
|
|
install the serializer :ref:`Symfony pack <symfony-packs>` before using it:
|
|
|
|
.. code-block:: terminal
|
|
|
|
$ composer require symfony/serializer-pack
|
|
|
|
.. note::
|
|
|
|
The serializer pack also installs some commonly used optional
|
|
dependencies of the Serializer component. When using this component
|
|
outside the Symfony framework, you might want to start with the
|
|
``symfony/serializer`` package and install optional dependencies if you
|
|
need them.
|
|
|
|
.. seealso::
|
|
|
|
A popular alternative to the Symfony Serializer component is the third-party
|
|
library, `JMS serializer`_.
|
|
|
|
Serializing an Object
|
|
---------------------
|
|
|
|
For this example, assume the following class exists in your project::
|
|
|
|
// src/Model/Person.php
|
|
namespace App\Model;
|
|
|
|
class Person
|
|
{
|
|
public function __construct(
|
|
private int $age,
|
|
private string $name,
|
|
private bool $sportsperson
|
|
) {
|
|
}
|
|
|
|
public function getAge(): int
|
|
{
|
|
return $this->age;
|
|
}
|
|
|
|
public function getName(): string
|
|
{
|
|
return $this->name;
|
|
}
|
|
|
|
public function isSportsperson(): bool
|
|
{
|
|
return $this->sportsperson;
|
|
}
|
|
}
|
|
|
|
If you want to transform objects of this type into a JSON structure (e.g.
|
|
to send them via an API response), get the ``serializer`` service by using
|
|
the :class:`Symfony\\Component\\Serializer\\SerializerInterface` parameter type:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-symfony
|
|
|
|
// src/Controller/PersonController.php
|
|
namespace App\Controller;
|
|
|
|
use App\Model\Person;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\Serializer\SerializerInterface;
|
|
|
|
class PersonController extends AbstractController
|
|
{
|
|
public function index(SerializerInterface $serializer): Response
|
|
{
|
|
$person = new Person('Jane Doe', 39, false);
|
|
|
|
$jsonContent = $serializer->serialize($person, 'json');
|
|
// $jsonContent contains {"name":"Jane Doe","age":39,"sportsperson":false}
|
|
|
|
return JsonResponse::fromJsonString($jsonContent);
|
|
}
|
|
}
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use App\Model\Person;
|
|
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
|
use Symfony\Component\Serializer\Serializer;
|
|
|
|
$encoders = [new JsonEncoder()];
|
|
$normalizers = [new ObjectNormalizer()];
|
|
$serializer = new Serializer($normalizers, $encoders);
|
|
|
|
$person = new Person('Jane Done', 39, false);
|
|
|
|
$jsonContent = $serializer->serialize($person, 'json');
|
|
// $jsonContent contains {"name":"Jane Doe","age":39,"sportsperson":false}
|
|
|
|
The first parameter of the :method:`Symfony\\Component\\Serializer\\Serializer::serialize`
|
|
is the object to be serialized and the second is used to choose the proper
|
|
encoder (i.e. format), in this case the :class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder`.
|
|
|
|
.. tip::
|
|
|
|
When your controller class extends ``AbstractController`` (like in the
|
|
example above), you can simplify your controller by using the
|
|
:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController::json`
|
|
method to create a JSON response from an object using the Serializer::
|
|
|
|
class PersonController extends AbstractController
|
|
{
|
|
public function index(): Response
|
|
{
|
|
$person = new Person('Jane Doe', 39, false);
|
|
|
|
// when the Serializer is not available, this will use json_encode()
|
|
return $this->json($person);
|
|
}
|
|
}
|
|
|
|
Using the Serializer in Twig Templates
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
You can also serialize objects in any Twig template using the ``serialize``
|
|
filter:
|
|
|
|
.. code-block:: twig
|
|
|
|
{{ person|serialize(format = 'json') }}
|
|
|
|
See the :ref:`twig reference <reference-twig-filter-serialize>` for more
|
|
information.
|
|
|
|
Deserializing an Object
|
|
-----------------------
|
|
|
|
APIs often also need to convert a formatted request body (e.g. JSON) to a
|
|
PHP object. This process is called *deserialization* (also known as "hydration"):
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-symfony
|
|
|
|
// src/Controller/PersonController.php
|
|
namespace App\Controller;
|
|
|
|
// ...
|
|
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
|
|
class PersonController extends AbstractController
|
|
{
|
|
// ...
|
|
|
|
public function create(Request $request, SerializerInterface $serializer): Response
|
|
{
|
|
if ('json' !== $request->getContentTypeFormat()) {
|
|
throw new BadRequestException('Unsupported content format');
|
|
}
|
|
|
|
$jsonData = $request->getContent();
|
|
$person = $serializer->deserialize($jsonData, Person::class, 'json');
|
|
|
|
// ... do something with $person and return a response
|
|
}
|
|
}
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use App\Model\Person;
|
|
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
|
use Symfony\Component\Serializer\Serializer;
|
|
|
|
// ...
|
|
$jsonData = ...; // fetch JSON from the request
|
|
$person = $serializer->deserialize($jsonData, Person::class, 'json');
|
|
|
|
In this case, :method:`Symfony\\Component\\Serializer\\Serializer::deserialize`
|
|
needs three parameters:
|
|
|
|
#. The data to be decoded
|
|
#. The name of the class this information will be decoded to
|
|
#. The name of the encoder used to convert the data to an array (i.e. the
|
|
input format)
|
|
|
|
When sending a request to this controller (e.g.
|
|
``{"first_name":"John Doe","age":54,"sportsperson":true}``), the serializer
|
|
will create a new instance of ``Person`` and sets the properties to the
|
|
values from the given JSON.
|
|
|
|
.. note::
|
|
|
|
By default, additional attributes that are not mapped to the
|
|
denormalized object will be ignored by the Serializer component. For
|
|
instance, if a request to the above controller contains ``{..., "city": "Paris"}``,
|
|
the ``city`` field will be ignored. You can also throw an exception in
|
|
these cases using the :ref:`serializer context <serializer-context>`
|
|
you'll learn about later.
|
|
|
|
.. seealso::
|
|
|
|
You can also deserialize data into an existing object instance (e.g.
|
|
when updating data). See :ref:`Deserializing in an Existing Object <serializer-populate-existing-object>`.
|
|
|
|
.. _serializer-process:
|
|
|
|
The Serialization Process: Normalizers and Encoders
|
|
---------------------------------------------------
|
|
|
|
The serializer uses a two-step process when (de)serializing objects:
|
|
|
|
.. raw:: html
|
|
|
|
<object data="_images/serializer/serializer_workflow.svg" type="image/svg+xml"
|
|
alt="A flow diagram showing how objects are serialized/deserialized. This is described in the subsequent paragraph."
|
|
></object>
|
|
|
|
In both directions, data is always first converted to an array. This splits
|
|
the process in two separate responsibilities:
|
|
|
|
Normalizers
|
|
These classes convert **objects** into **arrays** and vice versa. They
|
|
do the heavy lifting of finding out which class properties to
|
|
serialize, what value they hold and what name they should have.
|
|
Encoders
|
|
Encoders convert **arrays** into a specific **format** and the other
|
|
way around. Each encoder knows exactly how to parse and generate a
|
|
specific format, for instance JSON or XML.
|
|
|
|
Internally, the ``Serializer`` class uses a sorted list of normalizers and
|
|
one encoder for the specific format when (de)serializing an object.
|
|
|
|
There are several normalizers configured in the default ``serializer``
|
|
service. The most important normalizer is the
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer`. This
|
|
normalizer uses reflection and the :doc:`PropertyAccess component </components/property_access>`
|
|
to transform between any object and an array. You'll learn more about
|
|
:ref:`this and other normalizers <serializer-normalizers>` later.
|
|
|
|
The default serializer is also configured with some encoders, covering the
|
|
common formats used by HTTP applications:
|
|
|
|
* :class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder`
|
|
* :class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder`
|
|
* :class:`Symfony\\Component\\Serializer\\Encoder\\CsvEncoder`
|
|
* :class:`Symfony\\Component\\Serializer\\Encoder\\YamlEncoder`
|
|
|
|
Read more about these encoders and their configuration in
|
|
:doc:`/serializer/encoders`.
|
|
|
|
.. tip::
|
|
|
|
The `API Platform`_ project provides encoders for more advanced
|
|
formats:
|
|
|
|
* `JSON-LD`_ along with the `Hydra Core Vocabulary`_
|
|
* `OpenAPI`_ v2 (formerly Swagger) and v3
|
|
* `GraphQL`_
|
|
* `JSON:API`_
|
|
* `HAL`_
|
|
|
|
.. _serializer-context:
|
|
|
|
Serializer Context
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
The serializer, and its normalizers and encoders, are configured through
|
|
the *serializer context*. This context can be configured in multiple
|
|
places:
|
|
|
|
* :ref:`Globally through the framework configuration <serializer-default-context>`
|
|
* :ref:`While serializing/deserializing <serializer-context-while-serializing-deserializing>`
|
|
* :ref:`For a specific property <serializer-using-context-builders>`
|
|
|
|
You can use all three options at the same time. When the same setting is
|
|
configured in multiple places, the latter in the list above will override
|
|
the previous one (e.g. the setting on a specific property overrides the one
|
|
configured globally).
|
|
|
|
.. _serializer-default-context:
|
|
|
|
Configure a Default Context
|
|
...........................
|
|
|
|
You can configure a default context in the framework configuration, for
|
|
instance to disallow extra fields while deserializing:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/serializer.yaml
|
|
framework:
|
|
serializer:
|
|
default_context:
|
|
allow_extra_attributes: false
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/serializer.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<container xmlns="http://symfony.com/schema/dic/services"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xmlns:framework="http://symfony.com/schema/dic/symfony"
|
|
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<framework:serializer>
|
|
<framework:default-context>
|
|
<framework:allow-extra-attributes>false</framework:allow-extra-attributes>
|
|
</framework:default-context>
|
|
</framework:serializer>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/serializer.php
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
$framework->serializer()
|
|
->defaultContext([
|
|
'allow_extra_attributes' => false,
|
|
])
|
|
;
|
|
};
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
|
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
|
|
|
// ...
|
|
$normalizers = [
|
|
new ObjectNormalizer(null, null, null, null, null, null, [
|
|
'allow_extra_attributes' => false,
|
|
]),
|
|
];
|
|
$serializer = new Serializer($normalizers, $encoders);
|
|
|
|
.. _serializer-context-while-serializing-deserializing:
|
|
|
|
Pass Context while Serializing/Deserializing
|
|
............................................
|
|
|
|
You can also configure the context for a single call to
|
|
``serialize()``/``deserialize()``. For instance, you can skip
|
|
properties with a ``null`` value only for one serialize call::
|
|
|
|
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
|
|
|
|
// ...
|
|
$serializer->serialize($person, 'json', [
|
|
AbstractObjectNormalizer::SKIP_NULL_VALUES => true
|
|
]);
|
|
|
|
// next calls to serialize() will NOT skip null values
|
|
|
|
.. _serializer-using-context-builders:
|
|
|
|
Using Context Builders
|
|
""""""""""""""""""""""
|
|
|
|
You can use "context builders" to help define the (de)serialization
|
|
context. Context builders are PHP objects that provide autocompletion,
|
|
validation, and documentation of context options::
|
|
|
|
use Symfony\Component\Serializer\Context\Normalizer\DateTimeNormalizerContextBuilder;
|
|
|
|
$contextBuilder = (new DateTimeNormalizerContextBuilder())
|
|
->withFormat('Y-m-d H:i:s');
|
|
$serializer->serialize($something, 'json', $contextBuilder->toArray());
|
|
|
|
Each normalizer/encoder has its related context builder. To create a more
|
|
complex (de)serialization context, you can chain them using the
|
|
``withContext()`` method::
|
|
|
|
use Symfony\Component\Serializer\Context\Encoder\CsvEncoderContextBuilder;
|
|
use Symfony\Component\Serializer\Context\Normalizer\ObjectNormalizerContextBuilder;
|
|
|
|
$initialContext = [
|
|
'custom_key' => 'custom_value',
|
|
];
|
|
|
|
$contextBuilder = (new ObjectNormalizerContextBuilder())
|
|
->withContext($initialContext)
|
|
->withGroups(['group1', 'group2']);
|
|
|
|
$contextBuilder = (new CsvEncoderContextBuilder())
|
|
->withContext($contextBuilder)
|
|
->withDelimiter(';');
|
|
|
|
$serializer->serialize($something, 'csv', $contextBuilder->toArray());
|
|
|
|
.. seealso::
|
|
|
|
You can also :doc:`create your context builders </serializer/custom_context_builders>`
|
|
to have autocompletion, validation, and documentation for your custom
|
|
context values.
|
|
|
|
Configure Context on a Specific Property
|
|
........................................
|
|
|
|
At last, you can also configure context values on a specific object
|
|
property. For instance, to configure the datetime format:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-attributes
|
|
|
|
// src/Model/Person.php
|
|
|
|
// ...
|
|
use Symfony\Component\Serializer\Attribute\Context;
|
|
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
|
|
|
|
class Person
|
|
{
|
|
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
|
|
public \DateTimeImmutable $createdAt;
|
|
|
|
// ...
|
|
}
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/serializer/person.yaml
|
|
App\Model\Person:
|
|
attributes:
|
|
createdAt:
|
|
contexts:
|
|
- context: { datetime_format: 'Y-m-d' }
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/serializer/person.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<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
|
|
https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
|
|
>
|
|
<class name="App\Model\Person">
|
|
<attribute name="createdAt">
|
|
<context>
|
|
<entry name="datetime_format">Y-m-d</entry>
|
|
</context>
|
|
</attribute>
|
|
</class>
|
|
</serializer>
|
|
|
|
.. note::
|
|
|
|
When using YAML or XML, the mapping files must be placed in one of
|
|
these locations:
|
|
|
|
* All ``*.yaml`` and ``*.xml`` files in the ``config/serializer/``
|
|
directory.
|
|
* The ``serialization.yaml`` or ``serialization.xml`` file in the
|
|
``Resources/config/`` directory of a bundle;
|
|
* All ``*.yaml`` and ``*.xml`` files in the ``Resources/config/serialization/``
|
|
directory of a bundle.
|
|
|
|
You can also specify a context specific to normalization or denormalization:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-attributes
|
|
|
|
// src/Model/Person.php
|
|
|
|
// ...
|
|
use Symfony\Component\Serializer\Attribute\Context;
|
|
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
|
|
|
|
class Person
|
|
{
|
|
#[Context(
|
|
normalizationContext: [DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'],
|
|
denormalizationContext: [DateTimeNormalizer::FORMAT_KEY => \DateTime::RFC3339],
|
|
)]
|
|
public \DateTimeImmutable $createdAt;
|
|
|
|
// ...
|
|
}
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/serializer/person.yaml
|
|
App\Model\Person:
|
|
attributes:
|
|
createdAt:
|
|
contexts:
|
|
- normalization_context: { datetime_format: 'Y-m-d' }
|
|
denormalization_context: { datetime_format: !php/const \DateTime::RFC3339 }
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/serializer/person.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<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
|
|
https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
|
|
>
|
|
<class name="App\Model\Person">
|
|
<attribute name="createdAt">
|
|
<normalization-context>
|
|
<entry name="datetime_format">Y-m-d</entry>
|
|
</normalization-context>
|
|
|
|
<denormalization-context>
|
|
<entry name="datetime_format">Y-m-d\TH:i:sP</entry>
|
|
</denormalization-context>
|
|
</attribute>
|
|
</class>
|
|
</serializer>
|
|
|
|
.. _serializer-context-group:
|
|
|
|
You can also restrict the usage of a context to some
|
|
:ref:`groups <serializer-groups-attribute>`:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-attributes
|
|
|
|
// src/Model/Person.php
|
|
|
|
// ...
|
|
use Symfony\Component\Serializer\Attribute\Context;
|
|
use Symfony\Component\Serializer\Attribute\Groups;
|
|
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
|
|
|
|
class Person
|
|
{
|
|
#[Groups(['extended'])]
|
|
#[Context([DateTimeNormalizer::FORMAT_KEY => \DateTime::RFC3339])]
|
|
#[Context(
|
|
context: [DateTimeNormalizer::FORMAT_KEY => \DateTime::RFC3339_EXTENDED],
|
|
groups: ['extended'],
|
|
)]
|
|
public \DateTimeImmutable $createdAt;
|
|
|
|
// ...
|
|
}
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/serializer/person.yaml
|
|
App\Model\Person:
|
|
attributes:
|
|
createdAt:
|
|
groups: [extended]
|
|
contexts:
|
|
- context: { datetime_format: !php/const \DateTime::RFC3339 }
|
|
- context: { datetime_format: !php/const \DateTime::RFC3339_EXTENDED }
|
|
groups: [extended]
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/serializer/person.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<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
|
|
https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
|
|
>
|
|
<class name="App\Model\Person">
|
|
<attribute name="createdAt">
|
|
<group>extended</group>
|
|
|
|
<context>
|
|
<entry name="datetime_format">Y-m-d\TH:i:sP</entry>
|
|
</context>
|
|
<context>
|
|
<entry name="datetime_format">Y-m-d\TH:i:s.vP</entry>
|
|
<group>extended</group>
|
|
</context>
|
|
</attribute>
|
|
</class>
|
|
</serializer>
|
|
|
|
The attribute can be repeated as much as needed on a single property.
|
|
Context without group is always applied first. Then context for the
|
|
matching groups are merged in the provided order.
|
|
|
|
If you repeat the same context in multiple properties, consider using the
|
|
``#[Context]`` attribute on your class to apply that context configuration to
|
|
all the properties of the class::
|
|
|
|
namespace App\Model;
|
|
|
|
use Symfony\Component\Serializer\Attribute\Context;
|
|
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
|
|
|
|
#[Context([DateTimeNormalizer::FORMAT_KEY => \DateTime::RFC3339])]
|
|
#[Context(
|
|
context: [DateTimeNormalizer::FORMAT_KEY => \DateTime::RFC3339_EXTENDED],
|
|
groups: ['extended'],
|
|
)]
|
|
class Person
|
|
{
|
|
// ...
|
|
}
|
|
|
|
Serializing JSON Using Streams
|
|
------------------------------
|
|
|
|
Symfony can encode PHP data structures to JSON streams and decode JSON streams
|
|
back into PHP data structures.
|
|
|
|
To do this, it relies on the :doc:`JsonStreamer component </serializer/streaming_json>`,
|
|
which is designed for high efficiency and can process large JSON data incrementally,
|
|
without needing to load the entire content into memory.
|
|
|
|
When deciding between the Serializer component and the JsonStreamer component,
|
|
consider the following:
|
|
|
|
* **Serializer Component**: Best suited for use cases that require flexibility,
|
|
such as dynamically manipulating object structures using normalizers and
|
|
denormalizers, or handling complex objects with multiple serialization
|
|
formats. It also supports output formats beyond JSON (including your own
|
|
custom ones).
|
|
* **JsonStreamer Component**: Best suited for simple objects and scenarios that
|
|
demand high performance and low memory usage. It's particularly effective
|
|
for processing very large JSON datasets or when streaming JSON in real-time
|
|
without loading the entire dataset into memory.
|
|
|
|
The choice depends on your specific use case. The JsonStreamer component is
|
|
tailored for performance and memory efficiency, whereas the Serializer
|
|
component provides greater flexibility and broader format support.
|
|
|
|
Read more about :doc:`streaming JSON </serializer/streaming_json>`.
|
|
|
|
Serializing to or from PHP Arrays
|
|
---------------------------------
|
|
|
|
The default :class:`Symfony\\Component\\Serializer\\Serializer` can also be
|
|
used to only perform one step of the :ref:`two step serialization process <serializer-process>`
|
|
by using the respective interface:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-symfony
|
|
|
|
use Symfony\Component\Serializer\Encoder\DecoderInterface;
|
|
use Symfony\Component\Serializer\Encoder\EncoderInterface;
|
|
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
|
// ...
|
|
|
|
class PersonController extends AbstractController
|
|
{
|
|
public function index(DenormalizerInterface&NormalizerInterface $serializer): Response
|
|
{
|
|
$person = new Person('Jane Doe', 39, false);
|
|
|
|
// use normalize() to convert a PHP object to an array
|
|
$personArray = $serializer->normalize($person, 'json');
|
|
|
|
// ...and denormalize() to convert an array back to a PHP object
|
|
$personCopy = $serializer->denormalize($personArray, Person::class);
|
|
|
|
// ...
|
|
}
|
|
|
|
public function json(DecoderInterface&EncoderInterface $serializer): Response
|
|
{
|
|
$data = ['name' => 'Jane Doe'];
|
|
|
|
// use encode() to transform PHP arrays into another format
|
|
$json = $serializer->encode($data, 'json');
|
|
|
|
// ...and decode() to transform any format to just PHP arrays (instead of objects)
|
|
$data = $serializer->decode('{"name":"Charlie Doe"}', 'json');
|
|
// $data contains ['name' => 'Charlie Doe']
|
|
}
|
|
}
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use App\Model\Person;
|
|
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
|
use Symfony\Component\Serializer\Serializer;
|
|
|
|
$encoders = [new JsonEncoder()];
|
|
$normalizers = [new ObjectNormalizer()];
|
|
$serializer = new Serializer($normalizers, $encoders);
|
|
|
|
// use normalize() to convert a PHP object to an array
|
|
$personArray = $serializer->normalize($person, 'json');
|
|
|
|
// ...and denormalize() to convert an array back to a PHP object
|
|
$personCopy = $serializer->denormalize($personArray, Person::class);
|
|
|
|
$data = ['name' => 'Jane Doe'];
|
|
|
|
// use encode() to transform PHP arrays into another format
|
|
$json = $serializer->encode($data, 'json');
|
|
|
|
// ...and decode() to transform any format to just PHP arrays (instead of objects)
|
|
$data = $serializer->decode('{"name":"Charlie Doe"}', 'json');
|
|
// $data contains ['name' => 'Charlie Doe']
|
|
|
|
.. _serializer_ignoring-attributes:
|
|
|
|
Ignoring Properties
|
|
-------------------
|
|
|
|
The ``ObjectNormalizer`` normalizes *all* properties of an object and all
|
|
methods starting with ``get*()``, ``has*()``, ``is*()`` and ``can*()``.
|
|
Some properties or methods should never be serialized. You can exclude
|
|
them using the ``#[Ignore]`` attribute:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-attributes
|
|
|
|
// src/Model/Person.php
|
|
namespace App\Model;
|
|
|
|
use Symfony\Component\Serializer\Attribute\Ignore;
|
|
|
|
class Person
|
|
{
|
|
// ...
|
|
|
|
#[Ignore]
|
|
public function isPotentiallySpamUser(): bool
|
|
{
|
|
// ...
|
|
}
|
|
}
|
|
|
|
.. code-block:: yaml
|
|
|
|
App\Model\Person:
|
|
attributes:
|
|
potentiallySpamUser:
|
|
ignore: true
|
|
|
|
.. code-block:: xml
|
|
|
|
<?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
|
|
https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
|
|
>
|
|
<class name="App\Model\Person">
|
|
<attribute name="potentiallySpamUser" ignore="true"/>
|
|
</class>
|
|
</serializer>
|
|
|
|
The ``potentiallySpamUser`` property will now never be serialized:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-symfony
|
|
|
|
use App\Model\Person;
|
|
|
|
// ...
|
|
$person = new Person('Jane Doe', 32, false);
|
|
$json = $serializer->serialize($person, 'json');
|
|
// $json contains {"name":"Jane Doe","age":32,"sportsperson":false}
|
|
|
|
$person1 = $serializer->deserialize(
|
|
'{"name":"Jane Doe","age":32,"sportsperson":false","potentiallySpamUser":false}',
|
|
Person::class,
|
|
'json'
|
|
);
|
|
// the "potentiallySpamUser" value is ignored
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use App\Model\Person;
|
|
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
|
|
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
|
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
|
use Symfony\Component\Serializer\Serializer;
|
|
|
|
// ...
|
|
|
|
// you need to pass a class metadata factory with a loader to the
|
|
// ObjectNormalizer when reading mapping information like Ignore or Groups.
|
|
// E.g. when using PHP attributes:
|
|
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
|
|
$normalizers = [new ObjectNormalizer($classMetadataFactory)];
|
|
|
|
$serializer = new Serializer($normalizers, $encoders);
|
|
|
|
$person = new Person('Jane Doe', 32, false);
|
|
$json = $serializer->serialize($person, 'json');
|
|
// $json contains {"name":"Jane Doe","age":32,"sportsperson":false}
|
|
|
|
$person1 = $serializer->deserialize(
|
|
'{"name":"Jane Doe","age":32,"sportsperson":false","potentiallySpamUser":false}',
|
|
Person::class,
|
|
'json'
|
|
);
|
|
// the "potentiallySpamUser" value is ignored
|
|
|
|
Ignoring Attributes Using the Context
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
You can also pass an array of attribute names to ignore at runtime using
|
|
the ``ignored_attributes`` context options::
|
|
|
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
|
|
|
// ...
|
|
$person = new Person('Jane Doe', 32, false);
|
|
$json = $serializer->serialize($person, 'json',
|
|
[
|
|
AbstractNormalizer::IGNORED_ATTRIBUTES => ['age'],
|
|
]);
|
|
// $json contains {"name":"Jane Doe","sportsperson":false}
|
|
|
|
However, this can quickly become unmaintainable if used excessively. See
|
|
the next section about *serialization groups* for a better solution.
|
|
|
|
.. _serializer-groups-attribute:
|
|
|
|
Selecting Specific Properties
|
|
-----------------------------
|
|
|
|
Instead of excluding a property or method in all situations, you might need
|
|
to exclude some properties in one place, but serialize them in another.
|
|
Groups are a handy way to achieve this.
|
|
|
|
You can add the ``#[Groups]`` attribute to your class:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-attributes
|
|
|
|
// src/Model/Person.php
|
|
namespace App\Model;
|
|
|
|
use Symfony\Component\Serializer\Attribute\Groups;
|
|
|
|
class Person
|
|
{
|
|
#[Groups(["admin-view"])]
|
|
private int $age;
|
|
|
|
#[Groups(["public-view"])]
|
|
private string $name;
|
|
|
|
#[Groups(["public-view"])]
|
|
private bool $sportsperson;
|
|
|
|
// ...
|
|
}
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/serializer/person.yaml
|
|
App\Model\Person:
|
|
attributes:
|
|
age:
|
|
groups: ['admin-view']
|
|
name:
|
|
groups: ['public-view']
|
|
sportsperson:
|
|
groups: ['public-view']
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/serializer/person.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<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
|
|
https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
|
|
>
|
|
<class name="App\Model\Person">
|
|
<attribute name="age">
|
|
<group>admin-view</group>
|
|
</attribute>
|
|
<attribute name="name">
|
|
<group>public-view</group>
|
|
</attribute>
|
|
<attribute name="sportsperson">
|
|
<group>public-view</group>
|
|
</attribute>
|
|
</class>
|
|
</serializer>
|
|
|
|
You can now choose which groups to use when serializing::
|
|
|
|
$json = $serializer->serialize(
|
|
$person,
|
|
'json',
|
|
['groups' => 'public-view']
|
|
);
|
|
// $json contains {"name":"Jane Doe","sportsperson":false}
|
|
|
|
// you can also pass an array of groups
|
|
$json = $serializer->serialize(
|
|
$person,
|
|
'json',
|
|
['groups' => ['public-view', 'admin-view']]
|
|
);
|
|
// $json contains {"name":"Jane Doe","age":32,"sportsperson":false}
|
|
|
|
// or use the special "*" value to select all groups
|
|
$json = $serializer->serialize(
|
|
$person,
|
|
'json',
|
|
['groups' => '*']
|
|
);
|
|
// $json contains {"name":"Jane Doe","age":32,"sportsperson":false}
|
|
|
|
Using the Serialization Context
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
At last, you can also use the ``attributes`` context option to select
|
|
properties at runtime::
|
|
|
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
|
// ...
|
|
|
|
$json = $serializer->serialize($person, 'json', [
|
|
AbstractNormalizer::ATTRIBUTES => ['name', 'company' => ['name']]
|
|
]);
|
|
// $json contains {"name":"Dunglas","company":{"name":"Les-Tilleuls.coop"}}
|
|
|
|
Only attributes that are :ref:`not ignored <serializer_ignoring-attributes>`
|
|
are available. If serialization groups are set, only attributes allowed by
|
|
those groups can be used.
|
|
|
|
.. _serializer-handling-arrays:
|
|
|
|
Handling Arrays
|
|
---------------
|
|
|
|
The serializer is capable of handling arrays of objects. Serializing arrays
|
|
works just like serializing a single object::
|
|
|
|
use App\Model\Person;
|
|
|
|
// ...
|
|
$person1 = new Person('Jane Doe', 39, false);
|
|
$person2 = new Person('John Smith', 52, true);
|
|
|
|
$persons = [$person1, $person2];
|
|
$jsonContent = $serializer->serialize($persons, 'json');
|
|
|
|
// $jsonContent contains [{"name":"Jane Doe","age":39,"sportsman":false},{"name":"John Smith","age":52,"sportsman":true}]
|
|
|
|
To deserialize a list of objects, you have to append ``[]`` to the type
|
|
parameter::
|
|
|
|
// ...
|
|
|
|
$jsonData = ...; // the serialized JSON data from the previous example
|
|
$persons = $serializer->deserialize($JsonData, Person::class.'[]', 'json');
|
|
|
|
For nested classes, you have to add a PHPDoc type to the property, constructor or setter::
|
|
|
|
// src/Model/UserGroup.php
|
|
namespace App\Model;
|
|
|
|
class UserGroup
|
|
{
|
|
/**
|
|
* @param Person[] $members
|
|
*/
|
|
public function __construct(
|
|
private array $members,
|
|
) {
|
|
}
|
|
|
|
// or if you're using a setter
|
|
|
|
/**
|
|
* @param Person[] $members
|
|
*/
|
|
public function setMembers(array $members): void
|
|
{
|
|
$this->members = $members;
|
|
}
|
|
|
|
// ...
|
|
}
|
|
|
|
.. tip::
|
|
|
|
The Serializer also supports array types used in static analysis, like
|
|
``list<Person>`` and ``array<Person>``. Make sure the
|
|
``phpstan/phpdoc-parser`` and ``phpdocumentor/reflection-docblock``
|
|
packages are installed (these are part of the ``symfony/serializer-pack``).
|
|
|
|
.. _serializer-nested-structures:
|
|
|
|
Deserializing Nested Structures
|
|
-------------------------------
|
|
|
|
Some APIs might provide verbose nested structures that you want to flatten
|
|
in the PHP object. For instance, imagine a JSON response like this:
|
|
|
|
.. code-block:: json
|
|
|
|
{
|
|
"id": "123",
|
|
"profile": {
|
|
"username": "jdoe",
|
|
"personal_information": {
|
|
"full_name": "Jane Doe"
|
|
}
|
|
}
|
|
}
|
|
|
|
You may wish to serialize this information to a single PHP object like::
|
|
|
|
class Person
|
|
{
|
|
private int $id;
|
|
private string $username;
|
|
private string $fullName;
|
|
}
|
|
|
|
Use the ``#[SerializedPath]`` to specify the path of the nested property
|
|
using :doc:`valid PropertyAccess syntax </components/property_access>`:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-attributes
|
|
|
|
namespace App\Model;
|
|
|
|
use Symfony\Component\Serializer\Attribute\SerializedPath;
|
|
|
|
class Person
|
|
{
|
|
private int $id;
|
|
|
|
#[SerializedPath('[profile][username]')]
|
|
private string $username;
|
|
|
|
#[SerializedPath('[profile][personal_information][full_name]')]
|
|
private string $fullName;
|
|
}
|
|
|
|
.. code-block:: yaml
|
|
|
|
App\Model\Person:
|
|
attributes:
|
|
username:
|
|
serialized_path: '[profile][username]'
|
|
fullName:
|
|
serialized_path: '[profile][personal_information][full_name]'
|
|
|
|
.. code-block:: xml
|
|
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<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
|
|
https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
|
|
>
|
|
<class name="App\Model\Person">
|
|
<attribute name="username" serialized-path="[profile][username]"/>
|
|
<attribute name="fullName" serialized-path="[profile][personal_information][full_name]"/>
|
|
</class>
|
|
</serializer>
|
|
|
|
.. warning::
|
|
|
|
The ``SerializedPath`` cannot be used in combination with a
|
|
``SerializedName`` for the same property.
|
|
|
|
The ``#[SerializedPath]`` attribute also applies to the serialization of a
|
|
PHP object::
|
|
|
|
use App\Model\Person;
|
|
// ...
|
|
|
|
$person = new Person(123, 'jdoe', 'Jane Doe');
|
|
$jsonContent = $serializer->serialize($person, 'json');
|
|
// $jsonContent contains {"id":123,"profile":{"username":"jdoe","personal_information":{"full_name":"Jane Doe"}}}
|
|
|
|
.. _serializer-name-conversion:
|
|
|
|
Converting Property Names when Serializing and Deserializing
|
|
------------------------------------------------------------
|
|
|
|
Sometimes serialized attributes must be named differently than properties
|
|
or getter/setter methods of PHP classes. This can be achieved using name
|
|
converters.
|
|
|
|
The serializer service uses the
|
|
:class:`Symfony\\Component\\Serializer\\NameConverter\\MetadataAwareNameConverter`.
|
|
With this name converter, you can change the name of an attribute using
|
|
the ``#[SerializedName]`` attribute:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-attributes
|
|
|
|
// src/Model/Person.php
|
|
namespace App\Model;
|
|
|
|
use Symfony\Component\Serializer\Attribute\SerializedName;
|
|
|
|
class Person
|
|
{
|
|
#[SerializedName('customer_name')]
|
|
private string $name;
|
|
|
|
// ...
|
|
}
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/serializer/person.yaml
|
|
App\Entity\Person:
|
|
attributes:
|
|
name:
|
|
serialized_name: customer_name
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/serializer/person.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<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
|
|
https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
|
|
>
|
|
<class name="App\Entity\Person">
|
|
<attribute name="name" serialized-name="customer_name"/>
|
|
</class>
|
|
</serializer>
|
|
|
|
This custom mapping is used to convert property names when serializing and
|
|
deserializing objects:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-symfony
|
|
|
|
// ...
|
|
|
|
$json = $serializer->serialize($person, 'json');
|
|
// $json contains {"customer_name":"Jane Doe", ...}
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use App\Model\Person;
|
|
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
|
|
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
|
|
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;
|
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
|
use Symfony\Component\Serializer\Serializer;
|
|
|
|
// ...
|
|
|
|
// Configure a loader to retrieve mapping information like SerializedName.
|
|
// E.g. when using PHP attributes:
|
|
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
|
|
$nameConverter = new MetadataAwareNameConverter($classMetadataFactory);
|
|
$normalizers = [
|
|
new ObjectNormalizer($classMetadataFactory, $nameConverter),
|
|
];
|
|
|
|
$serializer = new Serializer($normalizers, $encoders);
|
|
|
|
$person = new Person('Jane Doe', 32, false);
|
|
$json = $serializer->serialize($person, 'json');
|
|
// $json contains {"customer_name":"Jane Doe", ...}
|
|
|
|
.. seealso::
|
|
|
|
You can also create a custom name converter class. Read more about this
|
|
in :doc:`/serializer/custom_name_converter`.
|
|
|
|
.. _using-camelized-method-names-for-underscored-attributes:
|
|
|
|
CamelCase to snake_case
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
In many formats, it's common to use underscores to separate words (also known
|
|
as snake_case). However, in Symfony applications is common to use camelCase to
|
|
name properties.
|
|
|
|
Symfony provides a built-in name converter designed to transform between
|
|
snake_case and CamelCased styles during serialization and deserialization
|
|
processes. You can use it instead of the metadata aware name converter by
|
|
setting the ``name_converter`` setting to
|
|
``serializer.name_converter.camel_case_to_snake_case``:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/serializer.yaml
|
|
framework:
|
|
serializer:
|
|
name_converter: 'serializer.name_converter.camel_case_to_snake_case'
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/serializer.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<container xmlns="http://symfony.com/schema/dic/services"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xmlns:framework="http://symfony.com/schema/dic/symfony"
|
|
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<framework:serializer
|
|
name-converter="serializer.name_converter.camel_case_to_snake_case"
|
|
/>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/serializer.php
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
$framework->serializer()
|
|
->nameConverter('serializer.name_converter.camel_case_to_snake_case')
|
|
;
|
|
};
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
|
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
|
|
|
// ...
|
|
$normalizers = [
|
|
new ObjectNormalizer(null, new CamelCaseToSnakeCaseNameConverter()),
|
|
];
|
|
$serializer = new Serializer($normalizers, $encoders);
|
|
|
|
snake_case to CamelCase
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
In Symfony applications, it is common to use camelCase for naming properties.
|
|
However some packages may follow a snake_case convention.
|
|
|
|
Symfony provides a built-in name converter designed to transform between
|
|
CamelCase and snake_case styles during serialization and deserialization
|
|
processes. You can use it instead of the metadata-aware name converter by
|
|
setting the ``name_converter`` setting to
|
|
``serializer.name_converter.snake_case_to_camel_case``:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/serializer.yaml
|
|
framework:
|
|
serializer:
|
|
name_converter: 'serializer.name_converter.snake_case_to_camel_case'
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/serializer.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<container xmlns="http://symfony.com/schema/dic/services"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xmlns:framework="http://symfony.com/schema/dic/symfony"
|
|
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<framework:serializer
|
|
name-converter="serializer.name_converter.snake_case_to_camel_case"
|
|
/>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/serializer.php
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
$framework->serializer()
|
|
->nameConverter('serializer.name_converter.snake_case_to_camel_case')
|
|
;
|
|
};
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter;
|
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
|
|
|
// ...
|
|
$normalizers = [
|
|
new ObjectNormalizer(null, new SnakeCaseToCamelCaseNameConverter()),
|
|
];
|
|
$serializer = new Serializer($normalizers, $encoders);
|
|
|
|
.. versionadded:: 7.2
|
|
|
|
The snake_case to CamelCase converter was introduced in Symfony 7.2.
|
|
|
|
.. _serializer-built-in-normalizers:
|
|
|
|
Serializer Normalizers
|
|
----------------------
|
|
|
|
By default, the serializer service is configured with the following
|
|
normalizers (in order of priority):
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\UnwrappingDenormalizer`
|
|
Can be used to only denormalize a part of the input, read more about
|
|
this :ref:`later in this article <serializer-unwrapping-denormalizer>`.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\ProblemNormalizer`
|
|
Normalizes :class:`Symfony\\Component\\ErrorHandler\\Exception\\FlattenException`
|
|
errors according to the API Problem spec `RFC 7807`_.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\UidNormalizer`
|
|
Normalizes objects that extend :class:`Symfony\\Component\\Uid\\AbstractUid`.
|
|
|
|
The default normalization format for objects that implement :class:`Symfony\\Component\\Uid\\Uuid`
|
|
is the `RFC 4122`_ format (example: ``d9e7a184-5d5b-11ea-a62a-3499710062d0``).
|
|
The default normalization format for objects that implement :class:`Symfony\\Component\\Uid\\Ulid`
|
|
is the Base 32 format (example: ``01E439TP9XJZ9RPFH3T1PYBCR8``).
|
|
You can change the string format by setting the serializer context option
|
|
``UidNormalizer::NORMALIZATION_FORMAT_KEY`` to ``UidNormalizer::NORMALIZATION_FORMAT_BASE58``,
|
|
``UidNormalizer::NORMALIZATION_FORMAT_BASE32`` or ``UidNormalizer::NORMALIZATION_FORMAT_RFC4122``.
|
|
|
|
Also it can denormalize ``uuid`` or ``ulid`` strings to :class:`Symfony\\Component\\Uid\\Uuid`
|
|
or :class:`Symfony\\Component\\Uid\\Ulid`. The format does not matter.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\DateTimeNormalizer`
|
|
This normalizes between :phpclass:`DateTimeInterface` objects (e.g.
|
|
:phpclass:`DateTime` and :phpclass:`DateTimeImmutable`) and strings,
|
|
integers or floats.
|
|
|
|
:phpclass:`DateTime` and :phpclass:`DateTimeImmutable`) into strings,
|
|
integers or floats. By default, it converts them to strings using the
|
|
`RFC 3339`_ format. Use ``DateTimeNormalizer::FORMAT_KEY`` and
|
|
``DateTimeNormalizer::TIMEZONE_KEY`` to change the format.
|
|
|
|
To convert the objects to integers or floats, set the serializer
|
|
context option ``DateTimeNormalizer::CAST_KEY`` to ``int`` or
|
|
``float``.
|
|
|
|
.. versionadded:: 7.1
|
|
|
|
The ``DateTimeNormalizer::CAST_KEY`` context option was introduced in Symfony 7.1.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\ConstraintViolationListNormalizer`
|
|
This normalizer converts objects that implement
|
|
:class:`Symfony\\Component\\Validator\\ConstraintViolationListInterface`
|
|
into a list of errors according to the `RFC 7807`_ standard.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\DateTimeZoneNormalizer`
|
|
This normalizer converts between :phpclass:`DateTimeZone` objects and strings that
|
|
represent the name of the timezone according to the `list of PHP timezones`_.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\DateIntervalNormalizer`
|
|
This normalizes between :phpclass:`DateInterval` objects and strings.
|
|
By default, the ``P%yY%mM%dDT%hH%iM%sS`` format is used. Use the
|
|
``DateIntervalNormalizer::FORMAT_KEY`` option to change this.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\FormErrorNormalizer`
|
|
This normalizer works with classes that implement
|
|
:class:`Symfony\\Component\\Form\\FormInterface`.
|
|
|
|
It will get errors from the form and normalize them according to the
|
|
API Problem spec `RFC 7807`_.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\TranslatableNormalizer`
|
|
This normalizer converts objects implementing :class:`Symfony\\Contracts\\Translation\\TranslatableInterface`
|
|
to a translated string using the :doc:`translator </translation>`.
|
|
|
|
You can define the locale to use to translate the object by setting the
|
|
``TranslatableNormalizer::NORMALIZATION_LOCALE_KEY`` context option.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\BackedEnumNormalizer`
|
|
This normalizer converts between :phpclass:`BackedEnum` enums and
|
|
strings or integers.
|
|
|
|
By default, an exception is thrown when data is not a valid backed enumeration. If you
|
|
want ``null`` instead, you can set the ``BackedEnumNormalizer::ALLOW_INVALID_VALUES`` option.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\NumberNormalizer`
|
|
This normalizer converts between :phpclass:`BcMath\\Number` or :phpclass:`GMP` objects and
|
|
strings or integers.
|
|
|
|
.. versionadded:: 7.3
|
|
|
|
The ``NumberNormalizer`` was introduced in Symfony 7.3.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\DataUriNormalizer`
|
|
This normalizer converts between :phpclass:`SplFileInfo` objects and a
|
|
`data URI`_ string (``data:...``) such that files can be embedded into
|
|
serialized data.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\JsonSerializableNormalizer`
|
|
This normalizer works with classes that implement :phpclass:`JsonSerializable`.
|
|
|
|
It will call the :phpmethod:`JsonSerializable::jsonSerialize` method and
|
|
then further normalize the result. This means that nested
|
|
:phpclass:`JsonSerializable` classes will also be normalized.
|
|
|
|
This normalizer is particularly helpful when you want to gradually migrate
|
|
from an existing codebase using simple :phpfunction:`json_encode` to the Symfony
|
|
Serializer by allowing you to mix which normalizers are used for which classes.
|
|
|
|
Unlike with :phpfunction:`json_encode` circular references can be handled.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\ArrayDenormalizer`
|
|
This denormalizer converts an array of arrays to an array of objects
|
|
(with the given type). See :ref:`Handling Arrays <serializer-handling-arrays>`.
|
|
|
|
Use :class:`Symfony\\Component\\PropertyInfo\\PropertyInfoExtractor` to provide
|
|
hints with annotations like ``@var Person[]``:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
|
|
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
|
|
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
|
|
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
|
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
|
|
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
|
|
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
|
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
|
use Symfony\Component\Serializer\Serializer;
|
|
|
|
$propertyInfo = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]);
|
|
$normalizers = [new ObjectNormalizer(new ClassMetadataFactory(new AttributeLoader()), null, null, $propertyInfo), new ArrayDenormalizer()];
|
|
|
|
$this->serializer = new Serializer($normalizers, [new JsonEncoder()]);
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer`
|
|
This is the most powerful default normalizer and used for any object
|
|
that could not be normalized by the other normalizers.
|
|
|
|
It leverages the :doc:`PropertyAccess Component </components/property_access>`
|
|
to read and write in the object. This allows it to access properties
|
|
directly or using getters, setters, hassers, issers, canners, adders and
|
|
removers. Names are generated by removing the ``get``, ``set``,
|
|
``has``, ``is``, ``add`` or ``remove`` prefix from the method name and
|
|
transforming the first letter to lowercase (e.g. ``getFirstName()`` ->
|
|
``firstName``).
|
|
|
|
During denormalization, it supports using the constructor as well as
|
|
the discovered methods.
|
|
|
|
.. danger::
|
|
|
|
Always make sure the ``DateTimeNormalizer`` is registered when
|
|
serializing the ``DateTime`` or ``DateTimeImmutable`` classes to avoid
|
|
excessive memory usage and exposing internal details.
|
|
|
|
Built-in Normalizers
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Besides the normalizers registered by default (see previous section), the
|
|
serializer component also provides some extra normalizers. You can register
|
|
these by defining a service and tag it with :ref:`serializer.normalizer <reference-dic-tags-serializer-normalizer>`.
|
|
For instance, to use the ``CustomNormalizer`` you have to define a service
|
|
like:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/services.yaml
|
|
services:
|
|
# ...
|
|
|
|
# if you're using autoconfigure, the tag will be automatically applied
|
|
Symfony\Component\Serializer\Normalizer\CustomNormalizer:
|
|
tags:
|
|
# register the normalizer with a high priority (called earlier)
|
|
- { name: 'serializer.normalizer', priority: 500 }
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/services.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<container xmlns="http://symfony.com/schema/dic/services"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd">
|
|
|
|
<services>
|
|
<!-- ... -->
|
|
|
|
<!-- if you're using autoconfigure, the tag will be automatically applied -->
|
|
<service id="Symfony\Component\Serializer\Normalizer\CustomNormalizer">
|
|
<!-- register the normalizer with a high priority (called earlier) -->
|
|
<tag name="serializer.normalizer"
|
|
priority="500"
|
|
/>
|
|
</service>
|
|
</services>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/services.php
|
|
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
|
|
|
use Symfony\Component\Serializer\Normalizer\CustomNormalizer;
|
|
|
|
return function(ContainerConfigurator $container) {
|
|
// ...
|
|
|
|
// if you're using autoconfigure, the tag will be automatically applied
|
|
$services->set(CustomNormalizer::class)
|
|
// register the normalizer with a high priority (called earlier)
|
|
->tag('serializer.normalizer', [
|
|
'priority' => 500,
|
|
])
|
|
;
|
|
};
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\CustomNormalizer`
|
|
This normalizer calls a method on the PHP object when normalizing. The
|
|
PHP object must implement :class:`Symfony\\Component\\Serializer\\Normalizer\\NormalizableInterface`
|
|
and/or :class:`Symfony\\Component\\Serializer\\Normalizer\\DenormalizableInterface`.
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer`
|
|
This normalizer is an alternative to the default ``ObjectNormalizer``.
|
|
It reads the content of the class by calling the "getters" (public
|
|
methods starting with ``get``, ``has``, ``is`` or ``can``). It will
|
|
denormalize data by calling the constructor and the "setters" (public
|
|
methods starting with ``set``).
|
|
|
|
Objects are normalized to a map of property names and values (names are
|
|
generated by removing the ``get`` prefix from the method name and transforming
|
|
the first letter to lowercase; e.g. ``getFirstName()`` -> ``firstName``).
|
|
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer`
|
|
This is yet another alternative to the ``ObjectNormalizer``. This
|
|
normalizer directly reads and writes public properties as well as
|
|
**private and protected** properties (from both the class and all of
|
|
its parent classes) by using `PHP reflection`_. It supports calling the
|
|
constructor during the denormalization process.
|
|
|
|
Objects are normalized to a map of property names to property values.
|
|
|
|
You can also limit the normalizer to only use properties with a specific
|
|
visibility (e.g. only public properties) using the
|
|
``PropertyNormalizer::NORMALIZE_VISIBILITY`` context option. You can set it
|
|
to any combination of the ``PropertyNormalizer::NORMALIZE_PUBLIC``,
|
|
``PropertyNormalizer::NORMALIZE_PROTECTED`` and
|
|
``PropertyNormalizer::NORMALIZE_PRIVATE`` constants::
|
|
|
|
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
|
|
// ...
|
|
|
|
$json = $serializer->serialize($person, 'json', [
|
|
// only serialize public properties
|
|
PropertyNormalizer::NORMALIZE_VISIBILITY => PropertyNormalizer::NORMALIZE_PUBLIC,
|
|
|
|
// serialize public and protected properties
|
|
PropertyNormalizer::NORMALIZE_VISIBILITY => PropertyNormalizer::NORMALIZE_PUBLIC | PropertyNormalizer::NORMALIZE_PROTECTED,
|
|
]);
|
|
|
|
Named Serializers
|
|
-----------------
|
|
|
|
.. versionadded:: 7.2
|
|
|
|
Named serializers were introduced in Symfony 7.2.
|
|
|
|
Sometimes, you may need multiple configurations for the serializer, such as
|
|
different default contexts, name converters, or sets of normalizers and encoders,
|
|
depending on the use case. For example, when your application communicates with
|
|
multiple APIs, each of which follows its own set of serialization rules.
|
|
|
|
You can achieve this by configuring multiple serializer instances using
|
|
the ``named_serializers`` option:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/serializer.yaml
|
|
framework:
|
|
serializer:
|
|
named_serializers:
|
|
api_client1:
|
|
name_converter: 'serializer.name_converter.camel_case_to_snake_case'
|
|
default_context:
|
|
enable_max_depth: true
|
|
api_client2:
|
|
default_context:
|
|
enable_max_depth: false
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/serializer.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<container xmlns="http://symfony.com/schema/dic/services"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xmlns:framework="http://symfony.com/schema/dic/symfony"
|
|
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<framework:serializer>
|
|
|
|
<framework:named-serializer
|
|
name="api_client1"
|
|
name-converter="serializer.name_converter.camel_case_to_snake_case"
|
|
>
|
|
<framework:default-context>
|
|
<framework:enable_max_depth>true</framework:enable_max_depth>
|
|
</framework:default-context>
|
|
</framework:named-serializer>
|
|
|
|
<framework:named-serializer name="api_client2">
|
|
<framework:default-context>
|
|
<framework:enable_max_depth>false</framework:enable_max_depth>
|
|
</framework:default-context>
|
|
</framework:named-serializer>
|
|
|
|
</framework:serializer>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/serializer.php
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
$framework->serializer()
|
|
->namedSerializer('api_client1')
|
|
->nameConverter('serializer.name_converter.camel_case_to_snake_case')
|
|
->defaultContext([
|
|
'enable_max_depth' => true,
|
|
])
|
|
;
|
|
$framework->serializer()
|
|
->namedSerializer('api_client2')
|
|
->defaultContext([
|
|
'enable_max_depth' => false,
|
|
])
|
|
;
|
|
};
|
|
|
|
You can inject these different serializer instances
|
|
using :ref:`named aliases <autowiring-multiple-implementations-same-type>`::
|
|
|
|
namespace App\Controller;
|
|
|
|
// ...
|
|
use Symfony\Component\DependencyInjection\Attribute\Target;
|
|
|
|
class PersonController extends AbstractController
|
|
{
|
|
public function index(
|
|
SerializerInterface $serializer, // default serializer
|
|
SerializerInterface $apiClient1Serializer, // api_client1 serializer
|
|
#[Target('apiClient2.serializer')] // api_client2 serializer
|
|
SerializerInterface $customName,
|
|
) {
|
|
// ...
|
|
}
|
|
}
|
|
|
|
By default, named serializers use the built-in set of normalizers and encoders,
|
|
just like the main serializer service. However, you can customize them by
|
|
registering additional normalizers or encoders for a specific named serializer.
|
|
To do that, add a ``serializer`` attribute to
|
|
the :ref:`serializer.normalizer <reference-dic-tags-serializer-normalizer>`
|
|
or :ref:`serializer.encoder <reference-dic-tags-serializer-encoder>` tags:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/services.yaml
|
|
services:
|
|
# ...
|
|
|
|
Symfony\Component\Serializer\Normalizer\CustomNormalizer:
|
|
tags:
|
|
# add this normalizer only to a specific named serializer
|
|
- serializer.normalizer: { serializer: 'api_client1' }
|
|
# add this normalizer to several named serializers
|
|
- serializer.normalizer: { serializer: [ 'api_client1', 'api_client2' ] }
|
|
# add this normalizer to all serializers, including the default one
|
|
- serializer.normalizer: { serializer: '*' }
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/services.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<container xmlns="http://symfony.com/schema/dic/services"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd">
|
|
|
|
<services>
|
|
<!-- ... -->
|
|
|
|
<service id="Symfony\Component\Serializer\Normalizer\CustomNormalizer">
|
|
<!-- add this normalizer only to a specific named serializer -->
|
|
<tag name="serializer.normalizer" serializer="api_client1"/>
|
|
|
|
<!-- add this normalizer to several named serializers -->
|
|
<tag name="serializer.normalizer" serializer="api_client1"/>
|
|
<tag name="serializer.normalizer" serializer="api_client2"/>
|
|
|
|
<!-- add this normalizer to all serializers, including the default one -->
|
|
<tag name="serializer.normalizer" serializer="*"/>
|
|
</service>
|
|
</services>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/services.php
|
|
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
|
|
|
use Symfony\Component\Serializer\Normalizer\CustomNormalizer;
|
|
|
|
return function(ContainerConfigurator $container) {
|
|
// ...
|
|
|
|
$services->set(CustomNormalizer::class)
|
|
// add this normalizer only to a specific named serializer
|
|
->tag('serializer.normalizer', ['serializer' => 'api_client1'])
|
|
// add this normalizer to several named serializers
|
|
->tag('serializer.normalizer', ['serializer' => ['api_client1', 'api_client2']])
|
|
// add this normalizer to all serializers, including the default one
|
|
->tag('serializer.normalizer', ['serializer' => '*'])
|
|
;
|
|
};
|
|
|
|
.. versionadded:: 7.3
|
|
|
|
Before Symfony 7.3, named serializer normalizers were added automatically
|
|
to the default serializer, so you had to set their ``autoconfigure``
|
|
option to ``false`` to disable them. As of Symfony 7.3, they are no longer
|
|
registered by default.
|
|
|
|
When the ``serializer`` attribute is not set, the service is registered only with
|
|
the default serializer.
|
|
|
|
Each normalizer or encoder used in a named serializer is tagged with a
|
|
``serializer.normalizer.<name>`` or ``serializer.encoder.<name>`` tag.
|
|
You can inspect their priorities using the following command:
|
|
|
|
.. code-block:: terminal
|
|
|
|
$ php bin/console debug:container --tag serializer.<normalizer|encoder>.<name>
|
|
|
|
Additionally, you can exclude the default set of normalizers and encoders from a
|
|
named serializer by setting the ``include_built_in_normalizers`` and
|
|
``include_built_in_encoders`` options to ``false``:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/serializer.yaml
|
|
framework:
|
|
serializer:
|
|
named_serializers:
|
|
api_client1:
|
|
include_built_in_normalizers: false
|
|
include_built_in_encoders: true
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/packages/serializer.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<container xmlns="http://symfony.com/schema/dic/services"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xmlns:framework="http://symfony.com/schema/dic/symfony"
|
|
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
|
https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
|
|
<framework:config>
|
|
<framework:serializer>
|
|
|
|
<framework:named-serializer
|
|
name="api_client1"
|
|
include-built-in-normalizers="false"
|
|
include-built-in-encoders="true"
|
|
/>
|
|
|
|
</framework:serializer>
|
|
</framework:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/serializer.php
|
|
use Symfony\Config\FrameworkConfig;
|
|
|
|
return static function (FrameworkConfig $framework): void {
|
|
$framework->serializer()
|
|
->namedSerializer('api_client1')
|
|
->includeBuiltInNormalizers(false)
|
|
->includeBuiltInEncoders(true)
|
|
;
|
|
};
|
|
|
|
Debugging the Serializer
|
|
------------------------
|
|
|
|
Use the ``debug:serializer`` command to dump the serializer metadata of a
|
|
given class:
|
|
|
|
.. code-block:: terminal
|
|
|
|
$ php bin/console debug:serializer 'App\Entity\Book'
|
|
|
|
App\Entity\Book
|
|
---------------
|
|
|
|
+----------+------------------------------------------------------------+
|
|
| Property | Options |
|
|
+----------+------------------------------------------------------------+
|
|
| name | [ |
|
|
| | "groups" => [ |
|
|
| | "book:read", |
|
|
| | "book:write", |
|
|
| | ], |
|
|
| | "maxDepth" => 1, |
|
|
| | "serializedName" => "book_name", |
|
|
| | "serializedPath" => null, |
|
|
| | "ignore" => false, |
|
|
| | "normalizationContexts" => [], |
|
|
| | "denormalizationContexts" => [] |
|
|
| | ] |
|
|
| isbn | [ |
|
|
| | "groups" => [ |
|
|
| | "book:read", |
|
|
| | ], |
|
|
| | "maxDepth" => null, |
|
|
| | "serializedName" => null, |
|
|
| | "serializedPath" => "[data][isbn]", |
|
|
| | "ignore" => false, |
|
|
| | "normalizationContexts" => [], |
|
|
| | "denormalizationContexts" => [] |
|
|
| | ] |
|
|
+----------+------------------------------------------------------------+
|
|
|
|
Advanced Serialization
|
|
----------------------
|
|
|
|
Skipping ``null`` Values
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
By default, the Serializer will preserve properties containing a ``null`` value.
|
|
You can change this behavior by setting the ``AbstractObjectNormalizer::SKIP_NULL_VALUES`` context option
|
|
to ``true``::
|
|
|
|
class Person
|
|
{
|
|
public string $name = 'Jane Doe';
|
|
public ?string $gender = null;
|
|
}
|
|
|
|
$jsonContent = $serializer->serialize(new Person(), 'json', [
|
|
AbstractObjectNormalizer::SKIP_NULL_VALUES => true,
|
|
]);
|
|
// $jsonContent contains {"name":"Jane Doe"}
|
|
|
|
Preserving Empty Objects
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
By default, the Serializer transforms an empty array to ``[]``. You can change
|
|
this behavior by setting the ``AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS``
|
|
context option to ``true``. When the value is an instance of ``\ArrayObject()``,
|
|
the serialized data will be ``{}``.
|
|
|
|
Handling Uninitialized Properties
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
In PHP, typed properties have an ``uninitialized`` state which is different
|
|
from the default ``null`` of untyped properties. When you try to access a typed
|
|
property before giving it an explicit value, you get an error.
|
|
|
|
To avoid the serializer throwing an error when serializing or normalizing
|
|
an object with uninitialized properties, by default the ``ObjectNormalizer``
|
|
catches these errors and ignores such properties.
|
|
|
|
You can disable this behavior by setting the
|
|
``AbstractObjectNormalizer::SKIP_UNINITIALIZED_VALUES`` context option to
|
|
``false``::
|
|
|
|
class Person {
|
|
public string $name = 'Jane Doe';
|
|
public string $phoneNumber; // uninitialized
|
|
}
|
|
|
|
$jsonContent = $normalizer->serialize(new Dummy(), 'json', [
|
|
AbstractObjectNormalizer::SKIP_UNINITIALIZED_VALUES => false,
|
|
]);
|
|
// throws Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException
|
|
// as the ObjectNormalizer cannot read uninitialized properties
|
|
|
|
.. note::
|
|
|
|
Using :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer`
|
|
or :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer`
|
|
with ``AbstractObjectNormalizer::SKIP_UNINITIALIZED_VALUES`` context
|
|
option set to ``false`` will throw an ``\Error`` instance if the given
|
|
object has uninitialized properties as the normalizers cannot read them
|
|
(directly or via getter/isser methods).
|
|
|
|
.. _component-serializer-handling-circular-references:
|
|
|
|
Handling Circular References
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Circular references are common when dealing with associated objects::
|
|
|
|
class Organization
|
|
{
|
|
public function __construct(
|
|
private string $name,
|
|
private array $members = []
|
|
) {
|
|
}
|
|
|
|
public function getName(): string
|
|
{
|
|
return $this->name;
|
|
}
|
|
|
|
public function addMember(Member $member): void
|
|
{
|
|
$this->members[] = $member;
|
|
}
|
|
|
|
public function getMembers(): array
|
|
{
|
|
return $this->members;
|
|
}
|
|
}
|
|
|
|
class Member
|
|
{
|
|
private Organization $organization;
|
|
|
|
public function __construct(
|
|
private string $name
|
|
) {
|
|
}
|
|
|
|
public function getName(): string
|
|
{
|
|
return $this->name;
|
|
}
|
|
|
|
public function setOrganization(Organization $organization): void
|
|
{
|
|
$this->organization = $organization;
|
|
}
|
|
|
|
public function getOrganization(): Organization
|
|
{
|
|
return $this->organization;
|
|
}
|
|
}
|
|
|
|
To avoid infinite loops, the normalizers throw a
|
|
:class:`Symfony\\Component\\Serializer\\Exception\\CircularReferenceException`
|
|
when such a case is encountered::
|
|
|
|
$organization = new Organization('Les-Tilleuls.coop');
|
|
$member = new Member('Kévin');
|
|
|
|
$organization->addMember($member);
|
|
$member->setOrganization($organization);
|
|
|
|
$jsonContent = $serializer->serialize($organization, 'json');
|
|
// throws a CircularReferenceException
|
|
|
|
The key ``circular_reference_limit`` in the context sets the number of
|
|
times it will serialize the same object before considering it a circular
|
|
reference. The default value is ``1``.
|
|
|
|
Instead of throwing an exception, circular references can also be handled
|
|
by custom callables. This is especially useful when serializing entities
|
|
having unique identifiers::
|
|
|
|
use Symfony\Component\Serializer\Exception\CircularReferenceException;
|
|
|
|
$context = [
|
|
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function (object $object, ?string $format, array $context): string {
|
|
if (!$object instanceof Organization) {
|
|
throw new CircularReferenceException('A circular reference has been detected when serializing the object of class "'.get_debug_type($object).'".');
|
|
}
|
|
|
|
// serialize the nested Organization with only the name (and not the members)
|
|
return $object->getName();
|
|
},
|
|
];
|
|
|
|
$jsonContent = $serializer->serialize($organization, 'json', $context);
|
|
// $jsonContent contains {"name":"Les-Tilleuls.coop","members":[{"name":"K\u00e9vin", organization: "Les-Tilleuls.coop"}]}
|
|
|
|
.. _serializer_handling-serialization-depth:
|
|
|
|
Handling Serialization Depth
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The serializer can also detect nested objects of the same class and limit
|
|
the serialization depth. This is useful for tree structures, where the same
|
|
object is nested multiple times.
|
|
|
|
For instance, assume a data structure of a family tree::
|
|
|
|
// ...
|
|
class Person
|
|
{
|
|
// ...
|
|
|
|
public function __construct(
|
|
private string $name,
|
|
private ?self $mother
|
|
) {
|
|
}
|
|
|
|
public function getName(): string
|
|
{
|
|
return $this->name;
|
|
}
|
|
|
|
public function getMother(): ?self
|
|
{
|
|
return $this->mother;
|
|
}
|
|
|
|
// ...
|
|
}
|
|
|
|
// ...
|
|
$greatGrandmother = new Person('Elizabeth', null);
|
|
$grandmother = new Person('Jane', $greatGrandmother);
|
|
$mother = new Person('Sophie', $grandmother);
|
|
$child = new Person('Joe', $mother);
|
|
|
|
You can specify the maximum depth for a given property. For instance, you
|
|
can set the max depth to ``1`` to always only serialize someone's mother
|
|
(and not their grandmother, etc.):
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-attributes
|
|
|
|
// src/Model/Person.php
|
|
namespace App\Model;
|
|
|
|
use Symfony\Component\Serializer\Attribute\MaxDepth;
|
|
|
|
class Person
|
|
{
|
|
#[MaxDepth(1)]
|
|
private ?self $mother;
|
|
|
|
// ...
|
|
}
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/serializer/person.yaml
|
|
App\Model\Person:
|
|
attributes:
|
|
mother:
|
|
max_depth: 1
|
|
|
|
.. code-block:: xml
|
|
|
|
<!-- config/serializer/person.xml -->
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<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
|
|
https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
|
|
>
|
|
<class name="App\Model\Person">
|
|
<attribute name="mother" max-depth="1"/>
|
|
</class>
|
|
</serializer>
|
|
|
|
To limit the serialization depth, you must set the
|
|
``AbstractObjectNormalizer::ENABLE_MAX_DEPTH`` key to ``true`` in the
|
|
context (or the default context specified in ``framework.yaml``)::
|
|
|
|
// ...
|
|
$greatGrandmother = new Person('Elizabeth', null);
|
|
$grandmother = new Person('Jane', $greatGrandmother);
|
|
$mother = new Person('Sophie', $grandmother);
|
|
$child = new Person('Joe', $mother);
|
|
|
|
$jsonContent = $serializer->serialize($child, null, [
|
|
AbstractObjectNormalizer::ENABLE_MAX_DEPTH => true
|
|
]);
|
|
// $jsonContent contains {"name":"Joe","mother":{"name":"Sophie"}}
|
|
|
|
You can also configure a custom callable that is used when the maximum
|
|
depth is reached. This can be used to for instance return the unique
|
|
identifier of the next nested object, instead of omitting the property::
|
|
|
|
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
|
|
// ...
|
|
|
|
$greatGrandmother = new Person('Elizabeth', null);
|
|
$grandmother = new Person('Jane', $greatGrandmother);
|
|
$mother = new Person('Sophie', $grandmother);
|
|
$child = new Person('Joe', $mother);
|
|
|
|
// all callback parameters are optional (you can omit the ones you don't use)
|
|
$maxDepthHandler = function (object $innerObject, object $outerObject, string $attributeName, ?string $format = null, array $context = []): ?string {
|
|
// return only the name of the next person in the tree
|
|
return $innerObject instanceof Person ? $innerObject->getName() : null;
|
|
};
|
|
|
|
$jsonContent = $serializer->serialize($child, null, [
|
|
AbstractObjectNormalizer::ENABLE_MAX_DEPTH => true,
|
|
AbstractObjectNormalizer::MAX_DEPTH_HANDLER => $maxDepthHandler,
|
|
]);
|
|
// $jsonContent contains {"name":"Joe","mother":{"name":"Sophie","mother":"Jane"}}
|
|
|
|
Using Callbacks to Serialize Properties with Object Instances
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
When serializing, you can set a callback to format a specific object
|
|
property. This can be used instead of
|
|
:ref:`defining the context for a group <serializer-context-group>`::
|
|
|
|
$person = new Person('cordoval', 34);
|
|
$person->setCreatedAt(new \DateTime('now'));
|
|
|
|
$context = [
|
|
AbstractNormalizer::CALLBACKS => [
|
|
// all callback parameters are optional (you can omit the ones you don't use)
|
|
'createdAt' => function (object $attributeValue, object $object, string $attributeName, ?string $format = null, array $context = []) {
|
|
return $attributeValue instanceof \DateTime ? $attributeValue->format(\DateTime::ATOM) : '';
|
|
},
|
|
],
|
|
];
|
|
$jsonContent = $serializer->serialize($person, 'json', $context);
|
|
// $jsonContent contains {"name":"cordoval","age":34,"createdAt":"2014-03-22T09:43:12-0500"}
|
|
|
|
Advanced Deserialization
|
|
------------------------
|
|
|
|
Require all Properties
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
By default, the Serializer will add ``null`` to nullable properties when
|
|
the parameters for those are not provided. You can change this behavior by
|
|
setting the ``AbstractNormalizer::REQUIRE_ALL_PROPERTIES`` context option
|
|
to ``true``::
|
|
|
|
class Person
|
|
{
|
|
public function __construct(
|
|
public string $firstName,
|
|
public ?string $lastName,
|
|
) {
|
|
}
|
|
}
|
|
|
|
// ...
|
|
$data = ['firstName' => 'John'];
|
|
$person = $serializer->deserialize($data, Person::class, 'json', [
|
|
AbstractNormalizer::REQUIRE_ALL_PROPERTIES => true,
|
|
]);
|
|
// throws Symfony\Component\Serializer\Exception\MissingConstructorArgumentException
|
|
|
|
Collecting Type Errors While Denormalizing
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
When denormalizing a payload to an object with typed properties, you'll get an
|
|
exception if the payload contains properties that don't have the same type as
|
|
the object.
|
|
|
|
Use the ``COLLECT_DENORMALIZATION_ERRORS`` option to collect all exceptions
|
|
at once, and to get the object partially denormalized::
|
|
|
|
try {
|
|
$person = $serializer->deserialize($jsonString, Person::class, 'json', [
|
|
DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS => true,
|
|
]);
|
|
} catch (PartialDenormalizationException $e) {
|
|
$violations = new ConstraintViolationList();
|
|
|
|
/** @var NotNormalizableValueException $exception */
|
|
foreach ($e->getErrors() as $exception) {
|
|
$message = sprintf('The type must be one of "%s" ("%s" given).', implode(', ', $exception->getExpectedTypes()), $exception->getCurrentType());
|
|
$parameters = [];
|
|
if ($exception->canUseMessageForUser()) {
|
|
$parameters['hint'] = $exception->getMessage();
|
|
}
|
|
$violations->add(new ConstraintViolation($message, '', $parameters, null, $exception->getPath(), null));
|
|
}
|
|
|
|
// ... return violation list to the user
|
|
}
|
|
|
|
.. _serializer-populate-existing-object:
|
|
|
|
Deserializing in an Existing Object
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The serializer can also be used to update an existing object. You can do
|
|
this by configuring the ``object_to_populate`` serializer context option::
|
|
|
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
|
|
|
// ...
|
|
$person = new Person('Jane Doe', 59);
|
|
|
|
$serializer->deserialize($jsonData, Person::class, 'json', [
|
|
AbstractNormalizer::OBJECT_TO_POPULATE => $person,
|
|
]);
|
|
// instead of returning a new object, $person is updated instead
|
|
|
|
.. note::
|
|
|
|
The ``AbstractNormalizer::OBJECT_TO_POPULATE`` option is only used for
|
|
the top level object. If that object is the root of a tree structure,
|
|
all child elements that exist in the normalized data will be re-created
|
|
with new instances.
|
|
|
|
When the ``AbstractObjectNormalizer::DEEP_OBJECT_TO_POPULATE`` context
|
|
option is set to ``true``, existing children of the root ``OBJECT_TO_POPULATE``
|
|
are updated from the normalized data, instead of the denormalizer
|
|
re-creating them. This only works for single child objects, not for
|
|
arrays of objects. Those will still be replaced when present in the
|
|
normalized data.
|
|
|
|
.. _serializer_interfaces-and-abstract-classes:
|
|
|
|
Deserializing Interfaces and Abstract Classes
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
When working with associated objects, a property sometimes reference an
|
|
interface or abstract class. When deserializing these properties, the
|
|
Serializer has to know which concrete class to initialize. This is done
|
|
using a *discriminator class mapping*.
|
|
|
|
Imagine there is an ``InvoiceItemInterface`` that is implemented by the
|
|
``Product`` and ``Shipping`` objects. When serializing an object, the
|
|
serializer will add an extra "discriminator attribute". This contains
|
|
either ``product`` or ``shipping``. The discriminator class map maps
|
|
these type names to the real PHP class name when deserializing:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-attributes
|
|
|
|
namespace App\Model;
|
|
|
|
use Symfony\Component\Serializer\Attribute\DiscriminatorMap;
|
|
|
|
#[DiscriminatorMap(
|
|
typeProperty: 'type',
|
|
mapping: [
|
|
'product' => Product::class,
|
|
'shipping' => Shipping::class,
|
|
]
|
|
)]
|
|
interface InvoiceItemInterface
|
|
{
|
|
// ...
|
|
}
|
|
|
|
.. code-block:: yaml
|
|
|
|
App\Model\InvoiceItemInterface:
|
|
discriminator_map:
|
|
type_property: type
|
|
mapping:
|
|
product: 'App\Model\Product'
|
|
shipping: 'App\Model\Shipping'
|
|
|
|
.. code-block:: xml
|
|
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<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
|
|
https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
|
|
>
|
|
<class name="App\Model\InvoiceItemInterface">
|
|
<discriminator-map type-property="type">
|
|
<mapping type="product" class="App\Model\Product"/>
|
|
<mapping type="shipping" class="App\Model\Shipping"/>
|
|
</discriminator-map>
|
|
</class>
|
|
</serializer>
|
|
|
|
With the discriminator map configured, the serializer can now pick the
|
|
correct class for properties typed as ``InvoiceItemInterface``::
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-symfony
|
|
|
|
class InvoiceLine
|
|
{
|
|
public function __construct(
|
|
private InvoiceItemInterface $invoiceItem
|
|
) {
|
|
$this->invoiceItem = $invoiceItem;
|
|
}
|
|
|
|
public function getInvoiceItem(): InvoiceItemInterface
|
|
{
|
|
return $this->invoiceItem;
|
|
}
|
|
|
|
// ...
|
|
}
|
|
|
|
// ...
|
|
$invoiceLine = new InvoiceLine(new Product());
|
|
|
|
$jsonString = $serializer->serialize($invoiceLine, 'json');
|
|
// $jsonString contains {"type":"product",...}
|
|
|
|
$invoiceLine = $serializer->deserialize($jsonString, InvoiceLine::class, 'json');
|
|
// $invoiceLine contains new InvoiceLine(new Product(...))
|
|
|
|
.. code-block:: php-standalone
|
|
|
|
// ...
|
|
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
|
|
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
|
|
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
|
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
|
use Symfony\Component\Serializer\Serializer;
|
|
|
|
class InvoiceLine
|
|
{
|
|
public function __construct(
|
|
private InvoiceItemInterface $invoiceItem
|
|
) {
|
|
$this->invoiceItem = $invoiceItem;
|
|
}
|
|
|
|
public function getInvoiceItem(): InvoiceItemInterface
|
|
{
|
|
return $this->invoiceItem;
|
|
}
|
|
|
|
// ...
|
|
}
|
|
|
|
// ...
|
|
|
|
// Configure a loader to retrieve mapping information like DiscriminatorMap.
|
|
// E.g. when using PHP attributes:
|
|
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
|
|
$discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
|
|
$normalizers = [
|
|
new ObjectNormalizer($classMetadataFactory, null, null, null, $discriminator),
|
|
];
|
|
|
|
$serializer = new Serializer($normalizers, $encoders);
|
|
|
|
$invoiceLine = new InvoiceLine(new Product());
|
|
|
|
$jsonString = $serializer->serialize($invoiceLine, 'json');
|
|
// $jsonString contains {"type":"product",...}
|
|
|
|
$invoiceLine = $serializer->deserialize($jsonString, InvoiceLine::class, 'json');
|
|
// $invoiceLine contains new InvoiceLine(new Product(...))
|
|
|
|
You can add a default type to avoid the need to add the type property
|
|
when deserializing:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php-attributes
|
|
|
|
namespace App\Model;
|
|
|
|
use Symfony\Component\Serializer\Attribute\DiscriminatorMap;
|
|
|
|
#[DiscriminatorMap(
|
|
typeProperty: 'type',
|
|
mapping: [
|
|
'product' => Product::class,
|
|
'shipping' => Shipping::class,
|
|
],
|
|
defaultType: 'product',
|
|
)]
|
|
interface InvoiceItemInterface
|
|
{
|
|
// ...
|
|
}
|
|
|
|
.. code-block:: yaml
|
|
|
|
App\Model\InvoiceItemInterface:
|
|
discriminator_map:
|
|
type_property: type
|
|
mapping:
|
|
product: 'App\Model\Product'
|
|
shipping: 'App\Model\Shipping'
|
|
default_type: product
|
|
|
|
.. code-block:: xml
|
|
|
|
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<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
|
|
https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
|
|
>
|
|
<class name="App\Model\InvoiceItemInterface">
|
|
<discriminator-map type-property="type" default-type="product">
|
|
<mapping type="product" class="App\Model\Product"/>
|
|
<mapping type="shipping" class="App\Model\Shipping"/>
|
|
</discriminator-map>
|
|
</class>
|
|
</serializer>
|
|
|
|
Now it deserializes like this:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: php
|
|
|
|
// $jsonString does NOT contain "type" in "invoiceItem"
|
|
$invoiceLine = $serializer->deserialize('{"invoiceItem":{...},...}', InvoiceLine::class, 'json');
|
|
// $invoiceLine contains new InvoiceLine(new Product(...))
|
|
|
|
.. versionadded:: 7.3
|
|
|
|
The ``defaultType`` parameter was added in Symfony 7.3.
|
|
|
|
.. _serializer-unwrapping-denormalizer:
|
|
|
|
Deserializing Input Partially (Unwrapping)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The serializer will always deserialize the complete input string into PHP
|
|
values. When connecting with third party APIs, you often only need a
|
|
specific part of the returned response.
|
|
|
|
To avoid deserializing the whole response, you can use the
|
|
:class:`Symfony\\Component\\Serializer\\Normalizer\\UnwrappingDenormalizer`
|
|
and "unwrap" the input data::
|
|
|
|
$jsonData = '{"result":"success","data":{"person":{"name": "Jane Doe","age":57}}}';
|
|
$data = $serialiser->deserialize($jsonData, Object::class, 'json', [
|
|
UnwrappingDenormalizer::UNWRAP_PATH => '[data][person]',
|
|
]);
|
|
// $data is Person(name: 'Jane Doe', age: 57)
|
|
|
|
The ``unwrap_path`` is a :ref:`property path <property-access-reading-arrays>`
|
|
of the PropertyAccess component, applied on the denormalized array.
|
|
|
|
Handling Constructor Arguments
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If the class constructor defines arguments, as usually happens with
|
|
`Value Objects`_, the serializer will match the parameter names with the
|
|
deserialized attributes. If some parameters are missing, a
|
|
:class:`Symfony\\Component\\Serializer\\Exception\\MissingConstructorArgumentsException`
|
|
is thrown.
|
|
|
|
In these cases, use the ``default_constructor_arguments`` context option to
|
|
define default values for the missing parameters::
|
|
|
|
use App\Model\Person;
|
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
|
// ...
|
|
|
|
$jsonData = '{"age":39,"name":"Jane Doe"}';
|
|
$person = $serializer->deserialize($jsonData, Person::class, 'json', [
|
|
AbstractNormalizer::DEFAULT_CONSTRUCTOR_ARGUMENTS => [
|
|
Person::class => ['sportsperson' => true],
|
|
],
|
|
]);
|
|
// $person is Person(name: 'Jane Doe', age: 39, sportsperson: true);
|
|
|
|
Recursive Denormalization and Type Safety
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
When a ``PropertyTypeExtractor`` is available, the normalizer will also
|
|
check that the data to denormalize matches the type of the property (even
|
|
for primitive types). For instance, if a ``string`` is provided, but the
|
|
type of the property is ``int``, an
|
|
:class:`Symfony\\Component\\Serializer\\Exception\\UnexpectedValueException`
|
|
will be thrown. The type enforcement of the properties can be disabled by
|
|
setting the serializer context option
|
|
``ObjectNormalizer::DISABLE_TYPE_ENFORCEMENT`` to ``true``.
|
|
|
|
Handling Boolean Values
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. versionadded:: 7.1
|
|
|
|
The ``AbstractNormalizer::FILTER_BOOL`` context option was introduced in Symfony 7.1.
|
|
|
|
PHP considers many different values as true or false. For example, the
|
|
strings ``true``, ``1``, and ``yes`` are considered true, while
|
|
``false``, ``0``, and ``no`` are considered false.
|
|
|
|
When deserializing, the Serializer component can take care of this
|
|
automatically. This can be done by using the ``AbstractNormalizer::FILTER_BOOL``
|
|
context option::
|
|
|
|
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
|
// ...
|
|
|
|
$person = $serializer->denormalize(['sportsperson' => 'yes'], Person::class, context: [
|
|
AbstractNormalizer::FILTER_BOOL => true
|
|
]);
|
|
// $person contains a Person instance with sportsperson set to true
|
|
|
|
This context makes the deserialization process behave like the
|
|
:phpfunction:`filter_var` function with the ``FILTER_VALIDATE_BOOL`` flag.
|
|
|
|
.. _serializer-enabling-metadata-cache:
|
|
|
|
Configuring the Metadata Cache
|
|
------------------------------
|
|
|
|
The metadata for the serializer is automatically cached to enhance application
|
|
performance. By default, the serializer uses the ``cache.system`` cache pool
|
|
which is configured using the :ref:`cache.system <reference-cache-system>`
|
|
option.
|
|
|
|
Going Further with the Serializer
|
|
---------------------------------
|
|
|
|
.. toctree::
|
|
:glob:
|
|
:maxdepth: 1
|
|
|
|
serializer/*
|
|
|
|
.. _`JMS serializer`: https://github.com/schmittjoh/serializer
|
|
.. _`API Platform`: https://api-platform.com
|
|
.. _`JSON-LD`: https://json-ld.org
|
|
.. _`Hydra Core Vocabulary`: https://www.hydra-cg.com/
|
|
.. _`OpenAPI`: https://www.openapis.org
|
|
.. _`GraphQL`: https://graphql.org
|
|
.. _`JSON:API`: https://jsonapi.org
|
|
.. _`HAL`: https://stateless.group/hal_specification.html
|
|
.. _`RFC 7807`: https://tools.ietf.org/html/rfc7807
|
|
.. _`RFC 4122`: https://tools.ietf.org/html/rfc4122
|
|
.. _`RFC 3339`: https://tools.ietf.org/html/rfc3339#section-5.8
|
|
.. _`list of PHP timezones`: https://www.php.net/manual/en/timezones.php
|
|
.. _`data URI`: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
|
|
.. _`PHP reflection`: https://php.net/manual/en/book.reflection.php
|
|
.. _`Value Objects`: https://en.wikipedia.org/wiki/Value_object
|