英文:
Preprocess property during deserialization
问题
让我们假设我有以下的类:
final class Foo
{
public string $bar;
}
以下是数据:
{
"bar": "SGVsbG8gd29ybGQ="
}
以及以下的Symfony/Serializer:
return new Serializer(
[
new ArrayDenormalizer(),
new ObjectNormalizer(
propertyTypeExtractor: new PropertyInfoExtractor(
typeExtractors: [
new ReflectionExtractor()
]
)
)
],
[new JsonEncoder()]
);
当反序列化数据时,将创建一个具有正确数据的对象:
$object = $serializer->deserialize('{"bar": "SGVsbG8gd29ybGQ="}', Foo::class, 'json');
$object
的类型是 Foo
,属性 $bar
包含 "SGVsbG8gd29ybGQ="
。
我想要"预处理"属性 $bar
并解码(base64)属性,而不是具有原始值,我想要的是 "Hello world"
。
我尝试使用自定义反规范化器的上下文,但没有成功。
有没有办法实现这个?
非常感谢。
问候。
英文:
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.
答案1
得分: 0
我终于找到了一个简单而有效的解决方案:使用一个正常化器,之后重新执行正常化。为了确保解码不会多次执行,上下文中添加了一个值。
<?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;
}
}
此自定义正常化器已添加到列表中:
return new Serializer(
[
new FooDenormalizer(),
new ArrayDenormalizer(),
new ObjectNormalizer(
propertyTypeExtractor: new PropertyInfoExtractor(
typeExtractors: [
new ReflectionExtractor()
]
)
)
],
[new JsonEncoder()]
);
当完成反序列化时,正常化器将被调用两次:
- 第一次解码该值
- 第二次跳过,因为上下文包含
self::ALREADY_CALLED
,其值为true
。
英文:
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:
- The first time the value is decoded
- The second time it is skipped because the context contains
self::ALREADY_CALLED
with the valuetrue
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论