在反序列化过程中预处理属性。

huangapple go评论131阅读模式
英文:

Preprocess property during deserialization

问题

让我们假设我有以下的类:

  1. final class Foo
  2. {
  3. public string $bar;
  4. }

以下是数据:

  1. {
  2. "bar": "SGVsbG8gd29ybGQ="
  3. }

以及以下的Symfony/Serializer:

  1. return new Serializer(
  2. [
  3. new ArrayDenormalizer(),
  4. new ObjectNormalizer(
  5. propertyTypeExtractor: new PropertyInfoExtractor(
  6. typeExtractors: [
  7. new ReflectionExtractor()
  8. ]
  9. )
  10. )
  11. ],
  12. [new JsonEncoder()]
  13. );

当反序列化数据时,将创建一个具有正确数据的对象:

  1. $object = $serializer->deserialize('{"bar": "SGVsbG8gd29ybGQ="}', Foo::class, 'json');

$object 的类型是 Foo,属性 $bar 包含 "SGVsbG8gd29ybGQ="

我想要"预处理"属性 $bar 并解码(base64)属性,而不是具有原始值,我想要的是 "Hello world"
我尝试使用自定义反规范化器的上下文,但没有成功。
有没有办法实现这个?

非常感谢。
问候。

英文:

Let say I have the following class:

  1. final class Foo
  2. {
  3. public string $bar;
  4. }

The following data

  1. {
  2. "bar": "SGVsbG8gd29ybGQ="
  3. }

And the follwoing symfony/serializer:

  1. return new Serializer(
  2. [
  3. new ArrayDenormalizer(),
  4. new ObjectNormalizer(
  5. propertyTypeExtractor: new PropertyInfoExtractor(
  6. typeExtractors: [
  7. new ReflectionExtractor()
  8. ]
  9. )
  10. )
  11. ],
  12. [new JsonEncoder()]
  13. );

When deserializing the data, an object with the correct data is created.

  1. $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

我终于找到了一个简单而有效的解决方案:使用一个正常化器,之后重新执行正常化。为了确保解码不会多次执行,上下文中添加了一个值。

  1. <?php
  2. declare(strict_types=1);
  3. use Symfony\Component\Serializer\Exception\BadMethodCallException;
  4. use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
  5. use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
  6. use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
  7. final class FooDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface
  8. {
  9. use DenormalizerAwareTrait;
  10. private const ALREADY_CALLED = 'FOO_PREPROCESS_ALREADY_CALLED';
  11. public function denormalize(mixed $data, string $type, string $format = null, array $context = [])
  12. {
  13. if ($this->denormalizer === null) {
  14. throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!');
  15. }
  16. $data['bar'] = base64_decode($data['bar']);
  17. $context[self::ALREADY_CALLED] = true;
  18. return $this->denormalizer->denormalize($data, $type, $format, $context);
  19. }
  20. public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
  21. {
  22. if ($context[self::ALREADY_CALLED] ?? false) {
  23. return false;
  24. }
  25. return $type === Foo::class;
  26. }
  27. }

此自定义正常化器已添加到列表中:

  1. return new Serializer(
  2. [
  3. new FooDenormalizer(),
  4. new ArrayDenormalizer(),
  5. new ObjectNormalizer(
  6. propertyTypeExtractor: new PropertyInfoExtractor(
  7. typeExtractors: [
  8. new ReflectionExtractor()
  9. ]
  10. )
  11. )
  12. ],
  13. [new JsonEncoder()]
  14. );

当完成反序列化时,正常化器将被调用两次:

  • 第一次解码该值
  • 第二次跳过,因为上下文包含 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.

  1. <?php
  2. declare(strict_types=1);
  3. use Symfony\Component\Serializer\Exception\BadMethodCallException;
  4. use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
  5. use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
  6. use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
  7. final class FooDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface
  8. {
  9. use DenormalizerAwareTrait;
  10. private const ALREADY_CALLED = 'FOO_PREPROCESS_ALREADY_CALLED';
  11. public function denormalize(mixed $data, string $type, string $format = null, array $context = [])
  12. {
  13. if ($this->denormalizer === null) {
  14. throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!');
  15. }
  16. $data['bar'] = base64_decode($data['bar']);
  17. $context[self::ALREADY_CALLED] = true;
  18. return $this->denormalizer->denormalize($data, $type, $format, $context);
  19. }
  20. public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
  21. {
  22. if ($context[self::ALREADY_CALLED] ?? false) {
  23. return false;
  24. }
  25. return $type === Foo::class;
  26. }
  27. }

This custom normalizer is added to the list:

  1. return new Serializer(
  2. [
  3. new FooDenormalizer(),
  4. new ArrayDenormalizer(),
  5. new ObjectNormalizer(
  6. propertyTypeExtractor: new PropertyInfoExtractor(
  7. typeExtractors: [
  8. new ReflectionExtractor()
  9. ]
  10. )
  11. )
  12. ],
  13. [new JsonEncoder()]
  14. );

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 value true

huangapple
  • 本文由 发表于 2023年8月4日 21:08:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76836229.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定