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

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

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 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:

确定