How the Serializer works

Last updated on
13 October 2016

The Serialization API depends on Symfony's Serializer component.

Serializer's separation of concerns

The serializer splits the work into two steps. First, it will normalize the object to an array. Then it will encode that array into the requested format.

Diagram showing object transformed to array by normalize method and array transformed to string by encode

This work is split across two interfaces, the NormalizerInterface and the EncoderInterface. When a Serializer is created, all of the Normalizer objects which might be necessary to handle the data are passed in, as well as all of the relevant Encoder objects.

$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new ComplexDataNormalizer(), new ListNormalizer(), new TypedDataNormalizer());

$serializer = new Serializer($normalizers, $encoders);

NOTE: Except in rare cases, you shouldn't create a new Serializer. Core adds a serializer service to the dependency injection container and modules should use and modify that Serializer.

How Serializer chooses the Normalizer to use

Normalizers can define which classes and formats they support. For example, there are three Normalizers in core.

  1. ComplexDataNormalizer for instances of ComplexDataInterface, e.g. entities.
  2. ListNormalizer for instances of ListInterface, e.g. fields.
  3. TypedDataNormalizer for instances of TypedDataInterface, e.g. field items.

All three of these normalizers support any format.

When Serializer::serialize() is called, the Serializer iterates through the list of Normalizers to see which it should use. It calls Normalizer::supportsNormalization($object, $format) on each Normalizer in the list until it finds one which returns TRUE.

For example, if you pass an entity to Serializer::serialize(), the ComplexDataNormalizer will be used because it is first in the list and it supports entities.

Diagram showing how Normalizer is chosen for entity

To handle the data contained in the fields on the entity, the ComplexDataNormalizer calls Serializer::normalize() itself, passing in each field object.

foreach ($object as $name => $field) {
  $attributes[$name] = $this->serializer->normalize($field, $format, $context);
}

Fields do not implement the ComplexDataInterface so they bypass the first Normalizer. The supportsNormalization function is then called on the ListNormalizer.

Diagram showing how Normalizer is chosen for field

Order is important here. Both fields and field items support TypedDataInterface, so if TypedDataNormalizer came before ListNormalizer it would be used for fields instead.

How Serializer chooses the Encoder to use

Once the object has been fully normalized, it is an array ready to be encoded. To determine the encoder to use, the Serializer uses the same process. It iterates through the list of Encoders, calling EncoderInterface::supportsEncoding($format) until it finds one which supports the format.

Help improve this page

Page status: No known problems

You can: