我尝试使用symfony序列化程序组件对具有关系的实体进行反序列化。这是我的实体:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Document
*
* @ORM\Table(name="document")
* @ORM\Entity(repositoryClass="AppBundle\Repository\DocumentRepository")
*/
class Document
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="Genre", inversedBy="documents")
* @ORM\JoinColumn(name="id_genre", referencedColumnName="id")
*/
private $genre;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=100)
*/
private $name;
//getters and setters down here
...
}
流派实体:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Genre
*
* @ORM\Table(name="genre")
* @ORM\Entity(repositoryClass="AppBundle\Repository\GenreRepository")
*/
class Genre
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=50, nullable=true)
*/
private $name;
/**
* @ORM\OneToMany(targetEntity="Document", mappedBy="genre")
*/
private $documents;
public function __construct()
{
$this->documents= new ArrayCollection();
}
//getters and setters down here
....
}
在我的控制器操作中,我现在尝试这样做:
$encoders = array(new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);
$document = $serializer->deserialize($request->getContent(), 'AppBundle\Entity\Document', 'json');
我的 json数据:
{"name": "My document", "genre": {"id": 1, "name": "My genre"}}
但我得到了下一个错误:
类型" AppBundle \ Entity \ Genre"," array"的预期参数给定(500 内部服务器错误)
是否可以使用内部关系的实体反序列化json请求?
先谢谢。
答案 0 :(得分:8)
是和否。首先,您不应该在控制器中重新创建序列化程序的新实例,而是使用serializer
服务。
其次,不能用Symfony序列化器开箱即用。我们是在https://api-platform.com/做的,但那里有一些魔力。也就是说,已经制定了PR来支持它:https://github.com/symfony/symfony/pull/19277
答案 1 :(得分:3)
现在可以使用。您必须在config.yml中启用property_info:
framework:
property_info:
enabled: true
答案 2 :(得分:1)
Symfony文档称之为“Recursive Denormalization”,从版本3.3开始到实际版本4.0。
为了让Symfony找到序列化对象的属性类型,需要使用PropertyInfo组件,正如@ slk500在他的回答中所述,必须在framework configuration中激活。
因此,如果您正在使用完整框架,那么为了反序列化嵌套的json对象,您需要做的就是:
1.启用config.yml中的序列化程序和属性信息组件:
framework:
#...
serializer: { enabled: true }
property_info: { enabled: true }
<?php
// src/AppBundle/Controller/DefaultController.php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function indexAction(SerializerInterface $serializer, Request $request)
{
$document = $serializer->deserialize($request->getContent(), 'AppBundle\Entity\Document', 'json');
// ...
}
}
这些组件的默认功能足以满足我的需求
自动装配负责基本服务声明,因此除非您需要特定的规范化器,否则您甚至不必编辑services.yml
配置文件。
根据您的使用情况,您可能必须启用特定功能。
检查Serializer和PropertyInfo文档(希望)更具体的用例。
答案 3 :(得分:0)
如果您使用的是JMS Serializer,则可以使用此代码,序列化程序将在数据库中搜索关系。
services.yml
services:
app.jms_doctrine_object_constructor:
class: AppBundle\Services\JMSDoctrineObjectConstructor
arguments: ['@doctrine', '@jms_serializer.unserialize_object_constructor']
jms_serializer.object_constructor:
alias: app.jms_doctrine_object_constructor
public: false
的appbundle \服务\ JMSDoctrineObjectConstructor.php
<?php
namespace AppBundle\Services;
use Doctrine\Common\Persistence\ManagerRegistry;
use JMS\Serializer\DeserializationContext;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\VisitorInterface;
use JMS\Serializer\Construction\ObjectConstructorInterface;
/**
* Doctrine object constructor for new (or existing) objects during deserialization.
*/
class JMSDoctrineObjectConstructor implements ObjectConstructorInterface
{
private $managerRegistry;
private $fallbackConstructor;
/**
* Constructor.
*
* @param ManagerRegistry $managerRegistry Manager registry
* @param ObjectConstructorInterface $fallbackConstructor Fallback object constructor
*/
public function __construct(ManagerRegistry $managerRegistry, ObjectConstructorInterface $fallbackConstructor)
{
$this->managerRegistry = $managerRegistry;
$this->fallbackConstructor = $fallbackConstructor;
}
/**
* {@inheritdoc}
*/
public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type, DeserializationContext $context)
{
// Locate possible ObjectManager
$objectManager = $this->managerRegistry->getManagerForClass($metadata->name);
if (!$objectManager) {
// No ObjectManager found, proceed with normal deserialization
return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
}
// Locate possible ClassMetadata
$classMetadataFactory = $objectManager->getMetadataFactory();
if ($classMetadataFactory->isTransient($metadata->name)) {
// No ClassMetadata found, proceed with normal deserialization
return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
}
// Managed entity, check for proxy load
if (!is_array($data)) {
// Single identifier, load proxy
return $objectManager->getReference($metadata->name, $data);
}
// Fallback to default constructor if missing identifier(s)
$classMetadata = $objectManager->getClassMetadata($metadata->name);
$identifierList = array();
foreach ($classMetadata->getIdentifierFieldNames() as $name) {
if (!array_key_exists($name, $data)) {
return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
}
$identifierList[$name] = $data[$name];
}
// Entity update, load it from database
if (array_key_exists('id', $identifierList) && $identifierList['id']) {
$object = $objectManager->find($metadata->name, $identifierList);
} else {
$object = new $metadata->name;
}
$objectManager->initializeObject($object);
return $object;
}
}