跳到内容

如何创建自定义规范化器

编辑此页

Serializer 组件 使用规范化器将任何数据转换为数组。该组件提供了几个 内置规范化器,但您可能需要创建自己的规范化器来转换不受支持的数据结构。

创建新的规范化器

假设您想在序列化过程中添加、修改或删除一些属性。为此,您必须创建自己的规范化器。但是,通常最好让 Symfony 规范化对象,然后挂钩到规范化以自定义规范化数据。为此,您可以注入 NormalizerInterface 并将其连接到 Symfony 的对象规范化器。这将使您可以访问 $normalizer 属性,该属性负责处理大部分规范化过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// src/Serializer/TopicNormalizer.php
namespace App\Serializer;

use App\Entity\Topic;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class TopicNormalizer implements NormalizerInterface
{
    public function __construct(
        #[Autowire(service: 'serializer.normalizer.object')]
        private readonly NormalizerInterface $normalizer,

        private UrlGeneratorInterface $router,
    ) {
    }

    public function normalize($topic, ?string $format = null, array $context = []): array
    {
        $data = $this->normalizer->normalize($topic, $format, $context);

        // Here, add, edit, or delete some data:
        $data['href']['self'] = $this->router->generate('topic_show', [
            'id' => $topic->getId(),
        ], UrlGeneratorInterface::ABSOLUTE_URL);

        return $data;
    }

    public function supportsNormalization($data, ?string $format = null, array $context = []): bool
    {
        return $data instanceof Topic;
    }

    public function getSupportedTypes(?string $format): array
    {
        return [
            Topic::class => true,
        ];
    }
}

在您的应用中注册它

在 Symfony 应用程序中使用此规范化器之前,必须将其注册为服务,并使用 serializer.normalizer 进行标记。如果您使用的是 默认 services.yaml 配置,则会自动完成此操作!

如果您不使用 autoconfigure,则必须使用 serializer.normalizer 标记该服务。您也可以使用此方法设置优先级(值越高表示在过程中调用得越早)

1
2
3
4
5
6
7
8
# config/services.yaml
services:
    # ...

    App\Serializer\TopicNormalizer:
        tags:
            # register the normalizer with a high priority (called earlier)
            - { name: 'serializer.normalizer', priority: 500 }

规范化器/反规范化器的性能

为了确定必须使用哪个规范化器(或反规范化器)来处理对象,Serializer 类将循环调用所有已注册规范化器(或反规范化器)的 supportsNormalization() (或 supportsDenormalization())。

此外,NormalizerInterfaceDenormalizerInterface 都包含 getSupportedTypes() 方法。此方法允许规范化器或反规范化器声明它们可以处理的对象类型,以及它们是否可缓存。有了这些信息,即使 supports*() 调用不可缓存,Serializer 也可以跳过大量对 supports*() 的方法调用,从而在某些情况下显着提高性能。

getSupportedTypes() 方法应返回一个数组,其中键表示支持的类型,值指示 supports*() 方法调用的结果是否可以缓存。返回数组的格式如下

  1. 特殊键 object 可用于指示规范化器或反规范化器支持任何类或接口。
  2. 特殊键 * 可用于指示规范化器或反规范化器可能支持任何类型。
  3. 数组中的其他键应对应于规范化器或反规范化器支持的特定类型。
  4. 与每种类型关联的值应为布尔值,指示该类型的 supports*() 方法调用的结果是否可以缓存。值 true 表示结果可缓存,而 false 表示结果不可缓存。
  5. 类型的 null 值表示规范化器或反规范化器不支持该类型。

以下是如何使用 getSupportedTypes() 方法的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class MyNormalizer implements NormalizerInterface
{
    // ...

    public function getSupportedTypes(?string $format): array
    {
        return [
            'object' => null,             // Doesn't support any classes or interfaces
            '*' => false,                 // Supports any other types, but the result is not cacheable
            MyCustomClass::class => true, // Supports MyCustomClass and result is cacheable
        ];
    }
}

注意

supports*() 方法的实现不应假设在之前已调用 getSupportedTypes()

本作品,包括代码示例,根据 Creative Commons BY-SA 3.0 许可获得许可。
TOC
    版本