After upgrading to Api platform 2.6, the non ApiResource
entities started being serialized (ld-json) along with a fake identifier and their type. For example:
{
"@type": "MetaData",
"@id": "_:11294",
"id": "bf1e417c-27ff-48c1-a591-40e5a43c708c",
"key": "key1",
"value": "value1"
}
we would like to remove these fields. To do so I've tried to implement a custom normalizer hoping to be able to hook in after the object is transformed in array and before the array is converted to a json.
Looking at the registered normalizers:
----------------------------------------------------------- ---------- ---------------------------------------------------------------------------
Service ID priority Class name
----------------------------------------------------------- ---------- ---------------------------------------------------------------------------
api_platform.serializer.normalizer.item -895 ApiPlatform\Core\Serializer\ItemNormalizer
api_platform.problem.normalizer.constraint_violation_list -780 ApiPlatform\Core\Problem\Serializer\ConstraintViolationListNormalizer
api_platform.hydra.normalizer.collection_filters -985 ApiPlatform\Core\Hydra\Serializer\CollectionFiltersNormalizer
api_platform.hydra.normalizer.error -800 ApiPlatform\Core\Hydra\Serializer\ErrorNormalizer
api_platform.hydra.normalizer.entrypoint -800 ApiPlatform\Core\Hydra\Serializer\EntrypointNormalizer
api_platform.hydra.normalizer.constraint_violation_list -780 ApiPlatform\Core\Hydra\Serializer\ConstraintViolationListNormalizer
api_platform.hydra.normalizer.documentation -800 ApiPlatform\Core\Hydra\Serializer\DocumentationNormalizer
api_platform.jsonld.normalizer.object -995 ApiPlatform\Core\JsonLd\Serializer\ObjectNormalizer
api_platform.swagger.normalizer.api_gateway -780 ApiPlatform\Core\Swagger\Serializer\ApiGatewayNormalizer
api_platform.openapi.normalizer.api_gateway -780 ApiPlatform\Core\Swagger\Serializer\ApiGatewayNormalizer
api_platform.serializer.uuid_denormalizer ApiPlatform\Core\Bridge\RamseyUuid\Serializer\UuidDenormalizer
serializer.denormalizer.array -990 Symfony\Component\Serializer\Normalizer\ArrayDenormalizer
serializer.normalizer.object -1000 Symfony\Component\Serializer\Normalizer\ObjectNormalizer
serializer.normalizer.problem -890 Symfony\Component\Serializer\Normalizer\ProblemNormalizer
serializer.normalizer.json_serializable -900 Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer
serializer.normalizer.datetime -910 Symfony\Component\Serializer\Normalizer\DateTimeNormalizer
serializer.normalizer.data_uri -920 Symfony\Component\Serializer\Normalizer\DataUriNormalizer
serializer.normalizer.dateinterval -915 Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer
serializer.normalizer.datetimezone -915 Symfony\Component\Serializer\Normalizer\DateTimeZoneNormalizer
serializer.normalizer.constraint_violation_list -915 Symfony\Component\Serializer\Normalizer\ConstraintViolationListNormalizer
api_platform.problem.normalizer.error -810 ApiPlatform\Core\Problem\Serializer\ErrorNormalizer
----------------------------------------------------------- ---------- ---------------------------------------------------------------------------
the closest I've got was around the api_platform.jsonld.normalizer.object
however if the priority I use is -994, I get the original object, if I use -996 I get the single string/boolean/numeric fields (so after the normalization).
Is there any way I can get the associative array so that I can remove @id and @type if @id starts with _:
?
The line of the code that adds those properties seems to be this however that probably just adds to the context it doesn't include those fields in the normalized array.
CodePudding user response:
No idea how to decorate the serializer, but I will handle your issue with an event subscriber.
The "POST_SERIALIZE" priority contains the serialized response body, and you can fetch it and modify it.
Like this:
<?php
namespace App\EventSubscriber;
use ApiPlatform\Core\EventListener\EventPriorities;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class PostSerializeProductEventSubScriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
KernelEvents::VIEW => ["removeFakeIdentifiersFromJsonLdResponseBody", EventPriorities::POST_SERIALIZE],
];
}
public function removeFakeIdentifiersFromJsonLdResponseBody(ViewEvent $event): void
{
$requestAcceptHeader = $event->getRequest()->headers->get('accept', 'none');
if (str_contains($requestAcceptHeader, "application/ld json")) {
$this->removefakeIdentifiersFromResponseBody($event);
}
}
private function removefakeIdentifiersFromResponseBody(ViewEvent $event): void
{
$responseBody = $event->getControllerResult();
$decodedResponseBody = json_decode($responseBody);
$this->removeFakeIdentifiersFromObject($decodedResponseBody);
$encodedResponseBody = json_encode($decodedResponseBody);
$event->setControllerResult($encodedResponseBody);
}
private function removeFakeIdentifiersFromObject(object $responseBody): void
{
foreach ($responseBody as $property => $value) {
$this->removeFakeIdRecursively($property, $value, $responseBody);
}
}
private function removeFakeIdRecursively(string $property, mixed $value, object $responseBody): void
{
if ($property === "@id" && str_starts_with($value, "_:")) {
unset($responseBody->$property);
} elseif (is_object($value)) {
$this->removeFakeIdentifiersFromObject($value);
} elseif (is_array($value)) {
foreach ($value as $object) {
if (is_object($object)) {
$this->removeFakeIdentifiersFromObject($object);
}
}
}
}
}