首页 > 解决方案 > symfony/serializer 中的泛型

问题描述

我使用 symfony 序列化程序来反序列化 REST 服务器对对象的回答。

服务器返回的数据是这样的(伪代码,答案本身是 JSON):

// Endpoint 1
class Paginated {
  public items:Object1[]
  public page:int
}

// Endpoint 2
class Paginated {
 public: items:Object2[]
 public page:int
}

因此,每个答案都包含在同一个“分页”对象中。

我不想重复每个对象中的公共成员,我想在我的 Symfony 应用程序中以与此伪代码中描述的相同方式实现对象。

问题是,PHP 不支持泛型来键入提示“items”成员,并且 symfony 序列化程序似乎没有提供类似的东西。

那么,解决这个问题的最佳方法是什么?

标签: phpsymfony

解决方案


解决此问题的一种简单方法是使用回调上下文选项。在该回调中,您可以自己反序列化要传递给 Paginated 的对象。然后,对于要在分页中支持的每种对象类型,您将拥有不同的回调并注册它。它不必像文档中显示的那样是一个闭包,您也可以使用一个类__invoke()来使其更容易在不同的地方重用。

另一种更一般地解决这个问题的方法是,通过编写一个自定义的非规范化器来实现DenormalizerAwareInterface(因此它可以将嵌套项的非规范化委托回序列化器。

就像 Symfony 可以识别Object[]对象列表一样,您可以创建自己的自定义类型约定来模拟 Generic。

假设您希望它类似于Paginated<Object1>,那么您的序列化程序调用可能看起来像这样:

$serializer->deserialize($json, Paginated::class . '<' . Object1::class . '>', 'json');

然后,您的(反)规范化器将支持与正则表达式匹配的类型。在 denormalize 方法中,您将获取 json 的数组结构,调用类似 `$denormalizedItems = $this->denormalizer->denormalize($data['items'], Object1::class . '[]'); 然后将它们放入您的分页对象中。大致是这样的:

public function denormalize($data, string $type, string $format = null, array $context = [])
{
    $extractedObjectType = ...; #extract class name inside <>

    $data['items'] = $this->denormalizer->denormalize($data['items'], $extractedType, $format, $context);

    // Option 1: Delegate denormalizing Paginated with the adjusted data
    return $this->denormalizer->denormalize($data, Paginated::class, $format, $context);

    // Option 2: Denormalize Paginated yourself and pass adjusted data as argument
    return new Paginated($data['items'], (int) $data['page']);
}

推荐阅读