【发布时间】:2020-12-24 18:13:31
【问题描述】:
Symfony 序列化程序在反规范化时丢失了集合对象。
例子:
我们在属性中有一些带有集合的实体(我使用的是 Doctrine ArrayCollection,但它会为任何实现 PHP Iterator 接口的集合复制)
让我们用 Symfony 和 JMS 序列化器对其进行序列化和反序列化并比较结果:
Origin:
^ App\Entity\Entity^ {#195
-rewardItems: Doctrine\Common\Collections\ArrayCollection^ {#179
-elements: array:2 [
0 => App\Entity\Item^ {#224
-type: 1
}
1 => App\Entity\Item^ {#180
-type: 2
}
]
}
}
Symfony deserialized:
^ App\Entity\Entity^ {#192
-rewardItems: array:2 [
0 => App\Entity\Item^ {#343
-type: 1
}
1 => App\Entity\Item^ {#325
-type: 2
}
]
}
Jms Deserialized
^ App\Entity\Entity^ {#369
-rewardItems: Doctrine\Common\Collections\ArrayCollection^ {#357
-elements: array:2 [
0 => App\Entity\Item^ {#374
-type: 1
}
1 => App\Entity\Item^ {#289
-type: 2
}
]
}
}
注意,原始实体和JMS反序列化实体都有内部ArrayCollection,而Symfony反序列化实体丢失了ArrayCollection并替换为phparray。
我只找到了一个解决方案:添加my own Symfony normalizer。但是我在这里看到非常糟糕的开发人员体验。对于我项目中的每个实体,我应该提供样板规范化器:(
symfony 是否有更简单的 Collections 解决方案(例如 JMS)?
复制立场:
- Symfony Framework 5.2 wit common flex configuration
symfony new serializer-demo
composer.phar require serializer doctrine jms\serializer
- 非常简单的实体
class Entity
{
/**
* @var ArrayCollection<\App\Entity\Item>
*
* @Type("ArrayCollection<App\Entity\Item>")
*
*/
private $rewardItems;
public function __construct()
{
$this->rewardItems = new ArrayCollection();
}
public function getRewardItems(): ArrayCollection
{
return $this->rewardItems;
}
public function setRewardItems($rewardItems): void
{
$this->rewardItems = $rewardItems;
}
}
class Item
{
/**
* @var int
* @Type("int")
*/
private $type;
public function __construct(int $type)
{
$this->type = $type;
}
/**
* @return int
*/
public function getType(): int
{
return $this->type;
}
/**
* @param int $type
*/
public function setType(int $type): void
{
$this->type = $type;
}
}
- 在控制台命令中运行
$a = new Entity();
$a->getRewardItems()->add(new Item(1));
$a->getRewardItems()->add(new Item(2));
$output->writeln("Origin:");
VarDumper::dump($a);
$this->symfony($a, $output);
$this->jms($a, $output);
// Symfony
$serializer = $this->serializer; // injected by DI
$serialized = $serializer->serialize($a, 'json');
$d = $serializer->deserialize($serialized, Entity::class, 'json');
$output->writeln("<warning>Symfony deserialized:</warning>");
VarDumper::dump($d);
// JMS
$serializer = SerializerBuilder::create()->build();
$jsonContent = $serializer->serialize($a, 'json');
$des = $serializer->deserialize($jsonContent, Entity::class, 'json');
$output->writeln("Jms Deserialized");
VarDumper::dump($des);```
UPD0:为 $rewardItems 属性更改结果提供 PHPDoc 类型提示,但没有真正帮助:
-
@var ArrayCollection<\App\Entity\Item>、ArrayCollection<Item>、@var Item[]- 结果与原始主题相同:
^ App\Entity\Entity^ {#192
-rewardItems: array:2 [
0 => App\Entity\Item^ {#343
-type: 1
}
1 => App\Entity\Item^ {#325
-type: 2
}
]
}
-
@var ArrayCollection<>结果有点不同:
App\Entity\Entity {#192
-rewardItems: Doctrine\Common\Collections\ArrayCollection {#307
-elements: []
}
}
【问题讨论】:
-
我会在这里跳出一个肢体,假设 jms 使用属性注释来正确猜测属性上的内容,而 symfony 序列化程序会注意到它有一个数组并且会用它调用 setRewardItems。我想知道如果你用
ArrayCollection输入参数会发生什么。无论如何,您都可以添加支票。另外,使用加法器而不是设置器可能会更好......我不喜欢在同样的意义上返回集合(因为它可能会微妙地导致对原始集合的修改),我不喜欢直接设置集合属性。 -
@Jakumi 感谢您的回答。我已经尝试了 PHPDoc 的所有可能组合(请参阅原始主题中的 UPD0)。它没有帮助
标签: php symfony serialization doctrine-orm