如何用Symfony Serializer仅深度序列化实体的第一层?
问题描述
我需要将对象序列化为JSON文本用于可追溯性存储,要求实体属性完整序列化,但关联对象仅保留ID,不深入序列化下层关联。按照Symfony官方文档配置了循环引用处理器后,执行序列化时出现内存耗尽、超时错误(Error: Maximum execution time of 30 seconds exceeded),怀疑是没正确设置默认序列化深度为1。
实体代码
<?php namespace App\Entity; use App\Repository\RegularServiceHoursRepository; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass=RegularServiceHoursRepository::class) */ class RegularServiceHours { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") */ private $id; /** * @ORM\Column(type="datetime") */ private $open; /** * @ORM\Column(type="datetime") */ private $close; /** * @var object \App\Entity\ACRGroup * * @ORM\ManyToOne(targetEntity="\App\Entity\ACRGroup", inversedBy="regularServiceHours") * @ORM\JoinColumn(name="acr_group", referencedColumnName="id", nullable=false) */ protected $ACRGroup; public function getId(): ?int { return $this->id; } public function getOpen(): ?\DateTimeInterface { return $this->open; } public function setOpen(\DateTimeInterface $open): self { $this->open = $open; return $this; } public function getClose(): ?\DateTimeInterface { return $this->close; } public function setClose(\DateTimeInterface $close): self { $this->close = $close; return $this; } /** * Set aCRGroup * * @param \App\Entity\ACRGroup $aCRGroup * * @return DebitPeriod */ public function setACRGroup(\App\Entity\ACRGroup $aCRGroup) { $this->ACRGroup = $aCRGroup; return $this; } /** * Get aCRGroup * * @return \App\Entity\ACRGroup */ public function getACRGroup() { return $this->ACRGroup; } /** * Get debitTimeSeconds * * @return int */ public function getTimeSeconds() { $open = $this->getOpen(); $close = $this->getClose(); $r = $close->format('U') - $open->format('U'); return $r; } }
控制器代码
use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Serializer; public function log($type,$message,$unit=null,$previous=null,$current=null) { //We only log successful operations. //If you want to log also errors, be very careful to not have anything persisted already, in que to be flushed. The flush below will store possible entities under conflict check and entities that sent you here due to a failed validation/conflict check, entities that weren't meant to be saved but rejected and forgotten. if ($type == 'success') { $encoder = new JsonEncoder(); $defaultContext = [ AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object, $format, $context) { return $object->getId(); }, ]; $normalizer = new ObjectNormalizer(null, null, null, null, null, null, $defaultContext); $serializer = new Serializer([$normalizer], [$encoder]); dd($serializer->serialize($current, 'json'));
解决方案
你的问题根源是仅配置了循环引用处理器,但未限制序列化深度,导致Serializer递归遍历所有关联对象的属性,即使无循环引用也会因深层关联触发内存溢出和超时。以下是两种可行的解决方式:
方案一:直接限制序列化深度
修改控制器中的defaultContext,添加MAX_DEPTH参数强制限制序列化层级:
$defaultContext = [ AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object, $format, $context) { return $object->getId(); }, AbstractNormalizer::MAX_DEPTH => 1, // 限制最大序列化深度为1 AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false, // 禁止序列化未定义属性 ];
方案二:精准处理关联对象序列化
针对关联对象类型单独定义序列化规则,直接返回ID,彻底避免递归:
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer; $defaultContext = [ PropertyNormalizer::CUSTOM_NORMALIZERS => [ // 对ACRGroup类型对象仅序列化ID \App\Entity\ACRGroup::class => function ($object) { return $object->getId(); }, ], ]; $normalizer = new PropertyNormalizer(null, null, null, null, $defaultContext); $serializer = new Serializer([$normalizer], [$encoder]);
额外优化:Symfony配置化管理(推荐)
如果是Symfony项目,建议通过配置文件定义序列化规则,避免硬编码。在config/packages/serializer.yaml中添加:
framework: serializer: default_context: circular_reference_handler: 'App\Serializer\CircularReferenceHandler' max_depth: 1 normalizers: App\Serializer\ACRGroupNormalizer: ~
然后创建ACRGroupNormalizer类:
<?php namespace App\Serializer; use App\Entity\ACRGroup; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class ACRGroupNormalizer implements NormalizerInterface { public function normalize($object, string $format = null, array $context = []) { return $object->getId(); } public function supportsNormalization($data, string $format = null) { return $data instanceof ACRGroup; } }
内容的提问来源于stack exchange,提问作者Matt Welander
相关产品推荐
相关产品推荐

