反序列化与Symfony Serializer Component关系的实体

时间:2016-06-10 06:21:05

标签: json doctrine-orm deserialization symfony

我尝试使用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请求?

先谢谢。

4 个答案:

答案 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 }
  1. 然后inject the serializer,无论您需要它:
  2. <?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;
    }
}
相关问题