# The Serialization Process ## Overall Process API Platform embraces and extends the Symfony Serializer Component to transform PHP entities in (hypermedia) API responses. The main serialization process has two stages: ![Serializer workflow](images/SerializerWorkflow.png) > As you can see in the picture above, an array is used as a man in the middle. This way, Encoders will only deal with turning specific formats into arrays and vice versa. The same way, Normalizers will deal with turning specific objects into arrays and vice versa. -- [The Symfony documentation](https://symfony.com/doc/current/components/serializer.html) Unlike Symfony itself, API Platform leverages custom normalizers, its router and the [data provider](data-providers.md) system to do an advanced tranformation. Metadata are added to the generated document including links, type information, pagination data or available filters. The API Platform Serializer is extendable, you can register custom normalizers and encoders to support other formats. You can also decorate existing normalizers to customize their behaviors. ## Available Serializers * [JSON-LD](https://json-ld.org) serializer `api_platform.jsonld.normalizer.item` JSON-LD, or JavaScript Object Notation for Linked Data, is a method of encoding Linked Data using JSON. It is a World Wide Web Consortium Recommendation. * [HAL](https://en.wikipedia.org/wiki/Hypertext_Application_Language) serializer `api_platform.hal.normalizer.item` * JSON, XML, CSV, YAML serializer (using the Symfony serializer) `api_platform.serializer.normalizer.item` ## The Serialization Context, Groups and Relations API Platform allows to specify the `$context` parameter used by the Symfony Serializer. This context has a handy `groups` key allowing to choose which attributes of the resource are exposed during the normalization (read) and denormalization (write) process. It relies on the [serialization (and deserialization) groups](https://symfony.com/doc/current/components/serializer.html#attributes-groups) feature of the Symfony Serializer component. In addition to groups, you can use any option supported by the Symfony Serializer such as [`enable_max_depth`](https://symfony.com/doc/current/components/serializer.html#handling-serialization-depth) to limit the serialization depth. ### Configuration Just like other Symfony and API Platform components, the Serializer can be configured using annotations, XML and YAML. As annotations are easy to understand and allow to group code and configuration, we will use them in the following examples. However, if you don't use the official distribution of API Platform, don't forget to enable annotation support in the serializer configuration: ```yaml # app/config/config.yml framework: serializer: { enable_annotations: true } ``` If you use [Symfony Flex](https://github.com/symfony/flex), just execute `composer req doctrine/annotations` and you are all set! ## Using Serialization Groups It is really simple to specify what groups to use in the API system: ```php decorated = $decorated; $this->authorizationChecker = $authorizationChecker; } public function createFromRequest(Request $request, bool $normalization, array $extractedAttributes = null) : array { $context = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes); $subject = $request->attributes->get('data'); if ($subject instanceof Book && isset($context['groups']) && $this->authorizationChecker->isGranted('ROLE_ADMIN') && false === $normalization) { $context['groups'][] = 'admin_input'; } return $context; } } ``` If the user has `ROLE_ADMIN` permission and the subject is an instance of Book, `admin_input` group will be dynamically added to the denormalization context. The variable `$normalization` lets you check whether the context is for normalization (if true) or denormalization. ## Name Conversion The Serializer Component provides a handy way to map PHP field names to serialized names. See the related [Symfony documentation](http://symfony.com/doc/master/components/serializer.html#converting-property-names-when-serializing-and-deserializing). To use this feature, declare a new service with id `app.name_converter`. For example, you can convert `CamelCase` to `snake_case` with the following configuration: ```yaml # app/config/services.yml services: 'Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter': ~ ``` ```yaml # app/config/config.yml api_platform: name_converter: 'Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter' ``` ## Decorating a Serializer and Add Extra Data In the following example, we will see how we add extra informations to the output. Here is how we add the date on each request in `GET`: ```yaml # app/config/services.yml services: 'AppBundle\Serializer\ApiNormalizer': decorates: 'api_platform.jsonld.normalizer.item' arguments: [ '@AppBundle\Serializer\ApiNormalizer.inner' ] ``` ```php decorated = $decorated; } public function supportsNormalization($data, $format = null) { return $this->decorated->supportsNormalization($data, $format); } public function normalize($object, $format = null, array $context = []) { $data = $this->decorated->normalize($object, $format, $context); if (is_array($data)) { $data['date'] = date(\DateTime::RFC3339); } return $data; } public function supportsDenormalization($data, $type, $format = null) { return $this->decorated->supportsDenormalization($data, $type, $format); } public function denormalize($data, $class, $format = null, array $context = []) { return $this->decorated->denormalize($data, $class, $format, $context); } public function setSerializer(SerializerInterface $serializer) { if($this->decorated instanceof SerializerAwareInterface) { $this->decorated->setSerializer($serializer); } } } ``` ## Entity Identifier Case API Platform is able to guess the entity identifier using [Doctrine metadata](http://doctrine-orm.readthedocs.org/en/latest/reference/basic-mapping.html#identifiers-primary-keys). It also supports composite identifiers. If Doctrine ORM is not used, the identifier must be marked explicitly using the `identifier` attribute of the `ApiPlatform\Core\Annotation\ApiProperty` annotation. In some cases, you will want to set the identifier of a resource from the client (e.g. a client-side generated UUID, or a slug). In this case the identifier property must become a writable class property. To do this you simply have to: * Create a setter for the identifier of the entity or make it a `public` property * Add the denormalization group to the property if you use a specific denormalization group * If you use Doctrine ORM, be sure to **not** mark this property with [the `@GeneratedValue` annotation](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#identifier-generation-strategies) or use the `NONE` value ## Embedding the JSON-LD Context By default, the generated [JSON-LD context](https://www.w3.org/TR/json-ld/#the-context) (`@context`) is only reference by an IRI. A client supporting JSON-LD must send a second HTTP request to retrieve it: ```json { "@context": "/contexts/Book", "@id": "/books/62", "@type": "Book", "name": "My awesome book", "author": "/people/59" } ``` You can configure API Platform to embed the JSON-LD context in the root document like the following: ```json { "@context": { "@vocab": "http://localhost:8000/apidoc#", "hydra": "http://www.w3.org/ns/hydra/core#", "name": "http://schema.org/name", "author": "http://schema.org/author" }, "@id": "/books/62", "@type": "Book", "name": "My awesome book", "author": "/people/59" } ``` To do so, use the following configuration: ```php