Let say I have the following class:
final class Foo
{
public string $bar;
}
The following data
{
"bar": "SGVsbG8gd29ybGQ="
}
And the follwoing symfony/serializer:
return new Serializer(
[
new ArrayDenormalizer(),
new ObjectNormalizer(
propertyTypeExtractor: new PropertyInfoExtractor(
typeExtractors: [
new ReflectionExtractor()
]
)
)
],
[new JsonEncoder()]
);
When deserializing the data, an object with the correct data is created.
$object = $serializer->deserialize('{"bar": "SGVsbG8gd29ybGQ="}', Foo::class, 'json');
$object
is of type Foo
and the property $bar
contains "SGVsbG8gd29ybGQ="
.
I would like to "preprocess" the property $bar
and decode (base64) the property and instead of having the raw value, I would like to have "Hello world"
.
I tried using the context of a custom denormalizer, but had no success.
Is there any way for achieving this?
Many thanks. Regards.
I finally found a simple and effective solution: use a normalizer which re-executes the normalization afterwards. To be sure that the decoding is not performed multiple times, a value in the context is added.
<?php
declare(strict_types=1);
use Symfony\Component\Serializer\Exception\BadMethodCallException;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
final class FooDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface
{
use DenormalizerAwareTrait;
private const ALREADY_CALLED = 'FOO_PREPROCESS_ALREADY_CALLED';
public function denormalize(mixed $data, string $type, string $format = null, array $context = [])
{
if ($this->denormalizer === null) {
throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!');
}
$data['bar'] = base64_decode($data['bar']);
$context[self::ALREADY_CALLED] = true;
return $this->denormalizer->denormalize($data, $type, $format, $context);
}
public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
{
if ($context[self::ALREADY_CALLED] ?? false) {
return false;
}
return $type === Foo::class;
}
}
This custom normalizer is added to the list:
return new Serializer(
[
new FooDenormalizer(),
new ArrayDenormalizer(),
new ObjectNormalizer(
propertyTypeExtractor: new PropertyInfoExtractor(
typeExtractors: [
new ReflectionExtractor()
]
)
)
],
[new JsonEncoder()]
);
When the deserialization is done, the normalizer will be called twice:
self::ALREADY_CALLED
with the value true