如何在Symfony 2.0 AJAX应用程序中将Doctrine实体编码为JSON?

Dmy*_*sun 87 ajax doctrine symfony doctrine-orm

我正在开发游戏应用程序并使用Symfony 2.0.我对后端有很多AJAX请求.更多的响应是将实体转换为JSON.例如:

class DefaultController extends Controller
{           
    public function launchAction()
    {   
        $user = $this->getDoctrine()
                     ->getRepository('UserBundle:User')                
                     ->find($id);

        // encode user to json format
        $userDataAsJson = $this->encodeUserDataToJson($user);
        return array(
            'userDataAsJson' => $userDataAsJson
        );            
    }

    private function encodeUserDataToJson(User $user)
    {
        $userData = array(
            'id' => $user->getId(),
            'profile' => array(
                'nickname' => $user->getProfile()->getNickname()
            )
        );

        $jsonEncoder = new JsonEncoder();        
        return $jsonEncoder->encode($userData, $format = 'json');
    }
}
Run Code Online (Sandbox Code Playgroud)

我的所有控制器都做同样的事情:获取一个实体并将其一些字段编码为JSON.我知道我可以使用规范化器并对所有权限进行编码.但是,如果一个实体已经循环链接到其他实体呢?或实体图非常大?你有什么建议吗?

我想一下实体的一些编码模式......或者NormalizableInterface用来避免循环..,

小智 147

有了php5.4,你现在可以做到:

use JsonSerializable;

/**
* @Entity(repositoryClass="App\Entity\User")
* @Table(name="user")
*/
class MyUserEntity implements JsonSerializable
{
    /** @Column(length=50) */
    private $name;

    /** @Column(length=50) */
    private $login;

    public function jsonSerialize()
    {
        return array(
            'name' => $this->name,
            'login'=> $this->login,
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

然后打电话

json_encode(MyUserEntity);
Run Code Online (Sandbox Code Playgroud)

  • 这似乎不适用于实体集合(即:`OneToMany`关系) (7认同)
  • 链接实体怎么样? (5认同)
  • 如果您想要将对其他捆绑包的依赖性降至最低,这是一个很好的解决方案...... (3认同)
  • 这违反了单一责任原则,如果您的实体是由学说自动生成的,则不好 (2认同)

Sof*_*fia 81

另一种选择是使用JMSSerializerBundle.然后在你的控制器中

$serializer = $this->container->get('serializer');
$reports = $serializer->serialize($doctrineobject, 'json');
return new Response($reports); // should be $reports as $doctrineobject is not serialized
Run Code Online (Sandbox Code Playgroud)

您可以通过在实体类中使用注释来配置序列化的完成方式.请参阅上面链接中的文档.例如,以下是排除链接实体的方法:

 /**
* Iddp\RorBundle\Entity\Report
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="Iddp\RorBundle\Entity\ReportRepository")
* @ExclusionPolicy("None")
*/
....
/**
* @ORM\ManyToOne(targetEntity="Client", inversedBy="reports")
* @ORM\JoinColumn(name="client_id", referencedColumnName="id")
* @Exclude
*/
protected $client;
Run Code Online (Sandbox Code Playgroud)

  • 你需要添加*使用JMS\SerializerBundle\Annotation\ExclusionPolicy; 在您的实体中使用JMS\SerializerBundle\Annotation\Exclude;*并安装JMSSerializerBundle以使其工作 (7认同)
  • 由于注释已从包中移出,因此现在使用正确的use语句:使用JMS\Serializer\Annotation\ExclusionPolicy; 使用JMS\Serializer\Annotation\Exclude; (7认同)
  • 如果将其更改为以下内容,则效果很好:返回新响应($ reports); (3认同)
  • Doctrine的文档说不要小心地序列化对象或序列化. (3认同)

web*_*a2l 39

您可以使用以下命令自动编码为复杂实体Json:

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;

$serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new 
JsonEncoder()));
$json = $serializer->serialize($entity, 'json');
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,但我有玩家实体,其中包含游戏实体集合的链接,每个游戏实体都有链接到玩家玩家.像这样的东西.你认为GetSetMethodNormalizer能正常工作吗(它使用递归算法)? (3认同)
  • 是的,它是递归的,这是我的问题.因此,对于特定实体,您可以使用CustomNormalizer及其NormalizableInterface,如您所知. (2认同)
  • 当我尝试这个时,我在/home/jason/pressbox/vendor/symfony/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php上得到了"致命错误:允许的内存大小为134217728字节耗尽(试图分配64个字节)"第44行".我想知道为什么? (2认同)

jer*_*ome 11

要完成答案:Symfony2附带了一个json_encode包装器: Symfony/Component/HttpFoundation/JsonResponse

控制器中的典型用法:

...
use Symfony\Component\HttpFoundation\JsonResponse;
...
public function acmeAction() {
...
return new JsonResponse($array);
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助

Ĵ


rkm*_*max 10

我发现序列化实体问题的解决方案如下:

#config/config.yml

services:
    serializer.method:
        class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer
    serializer.encoder.json:
        class: Symfony\Component\Serializer\Encoder\JsonEncoder
    serializer:
        class: Symfony\Component\Serializer\Serializer
        arguments:
            - [@serializer.method]
            - {json: @serializer.encoder.json }
Run Code Online (Sandbox Code Playgroud)

在我的控制器中:

$serializer = $this->get('serializer');

$entity = $this->get('doctrine')
               ->getRepository('myBundle:Entity')
               ->findOneBy($params);


$collection = $this->get('doctrine')
               ->getRepository('myBundle:Entity')
               ->findBy($params);

$toEncode = array(
    'response' => array(
        'entity' => $serializer->normalize($entity),
        'entities' => $serializer->normalize($collection)
    ),
);

return new Response(json_encode($toEncode));
Run Code Online (Sandbox Code Playgroud)

其他例子:

$serializer = $this->get('serializer');

$collection = $this->get('doctrine')
               ->getRepository('myBundle:Entity')
               ->findBy($params);

$json = $serializer->serialize($collection, 'json');

return new Response($json);
Run Code Online (Sandbox Code Playgroud)

您甚至可以将其配置为在http://api.symfony.com/2.0中反序列化数组

  • 有关于在Symfony 2.3+中使用Serializer组件的菜谱条目,因为您现在可以激活内置的:http://symfony.com/doc/current/cookbook/serializer.html (3认同)

oxy*_*oxy 6

我只需要解决同样的问题:json编码一个实体("用户"),它具有与另一个实体("位置")的一对多双向关联.

我尝试了几件事,我想现在我找到了最好的解决方案.我的想法是使用David编写的相同代码,但是通过告诉Normalizer在某个时刻停止来以某种方式拦截无限递归.

我不想实现自定义规范化器,因为在我看来这个GetSetMethodNormalizer是一个很好的方法(基于反射等).所以我决定将它子类化,这在初看起来并非无足轻重,因为说明是否包含属性(isGetMethod)的方法是私有的.

但是,可以覆盖normalize方法,所以我在这一点上拦截,只需取消设置引用"Location"的属性 - 因此无限循环被中断.

在代码中它看起来像这样:

class GetSetMethodNormalizer extends \Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer {

    public function normalize($object, $format = null)
    {
        // if the object is a User, unset location for normalization, without touching the original object
        if($object instanceof \Leonex\MoveBundle\Entity\User) {
            $object = clone $object;
            $object->setLocations(new \Doctrine\Common\Collections\ArrayCollection());
        }

        return parent::normalize($object, $format);
    }

} 
Run Code Online (Sandbox Code Playgroud)


Jul*_*tré 6

我有同样的问题,我选择创建自己的编码器,它将自己应对递归.

我创建了实现的类Symfony\Component\Serializer\Normalizer\NormalizerInterface,以及保存每个类的服务NormalizerInterface.

#This is the NormalizerService

class NormalizerService 
{

   //normalizer are stored in private properties
   private $entityOneNormalizer;
   private $entityTwoNormalizer;

   public function getEntityOneNormalizer()
   {
    //Normalizer are created only if needed
    if ($this->entityOneNormalizer == null)
        $this->entityOneNormalizer = new EntityOneNormalizer($this); //every normalizer keep a reference to this service

    return $this->entityOneNormalizer;
   }

   //create a function for each normalizer



  //the serializer service will also serialize the entities 
  //(i found it easier, but you don't really need it)
   public function serialize($objects, $format)
   {
     $serializer = new Serializer(
            array(
                $this->getEntityOneNormalizer(),
                $this->getEntityTwoNormalizer()
            ),
            array($format => $encoder) );

     return $serializer->serialize($response, $format);
}
Run Code Online (Sandbox Code Playgroud)

规范化器的一个示例:

use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class PlaceNormalizer implements NormalizerInterface {

private $normalizerService;

public function __construct($normalizerService)
{
    $this->service = normalizerService;

}

public function normalize($object, $format = null) {
    $entityTwo = $object->getEntityTwo();
    $entityTwoNormalizer = $this->service->getEntityTwoNormalizer();

    return array(
        'param' => object->getParam(),
        //repeat for every parameter
        //!!!! this is where the entityOneNormalizer dealt with recursivity
        'entityTwo' => $entityTwoNormalizer->normalize($entityTwo, $format.'_without_any_entity_one') //the 'format' parameter is adapted for ignoring entity one - this may be done with different ways (a specific method, etc.)
    );
}

}
Run Code Online (Sandbox Code Playgroud)

在控制器中:

$normalizerService = $this->get('normalizer.service'); //you will have to configure services.yml
$json = $normalizerService->serialize($myobject, 'json');
return new Response($json);
Run Code Online (Sandbox Code Playgroud)

完整的代码在这里:https://github.com/progracqteur/WikiPedale/tree/master/src/Progracqteur/WikipedaleBundle/Resources/Normalizer


Leb*_*nik 6

在Symfony 2.3中

/app/config/config.yml

framework:
    # ?????? ??????????????? ???????? ? ???????, json, xml ? ???????
    serializer:
        enabled: true

services:
    object_normalizer:
        class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer
        tags:
        # ???????? ? ???? ????????? ???? ??????, ??? ??. ?????, ?.?. ????? ???????? ?? ?????
          - { name: serializer.normalizer }
Run Code Online (Sandbox Code Playgroud)

以及控制器的示例:

/**
 * ????? ???????? ?? ?? ??????? ? ?? ?????
 * @Route("/search/", name="orgunitSearch")
 */
public function orgunitSearchAction()
{
    $array = $this->get('request')->query->all();

    $entity = $this->getDoctrine()
        ->getRepository('IntranetOrgunitBundle:Orgunit')
        ->findOneBy($array);

    $serializer = $this->get('serializer');
    //$json = $serializer->serialize($entity, 'json');
    $array = $serializer->normalize($entity);

    return new JsonResponse( $array );
}
Run Code Online (Sandbox Code Playgroud)

但字段类型\ DateTime的问题将保留.


mbo*_*ouz 6

这更像是一个更新(对于Symfony v:2.7+和JmsSerializer v:0.13.*@ dev),所以为了避免Jms尝试加载和序列化整个对象图(或者在循环关系的情况下......)

模型:

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\ExclusionPolicy;  
use JMS\Serializer\Annotation\Exclude;  
use JMS\Serializer\Annotation\MaxDepth; /* <=== Required */
/**
 * User
 *
 * @ORM\Table(name="user_table")
///////////////// OTHER Doctrine proprieties //////////////
 */
 public class User
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected   $id;

    /**
     * @ORM\ManyToOne(targetEntity="FooBundle\Entity\Game")
     * @ORM\JoinColumn(nullable=false)
     * @MaxDepth(1)
     */
    protected $game;
   /*
      Other proprieties ....and Getters ans setters
      ......................
      ......................
   */
Run Code Online (Sandbox Code Playgroud)

内部动作:

use JMS\Serializer\SerializationContext;
  /* Necessary include to enbale max depth */

  $users = $this
              ->getDoctrine()
              ->getManager()
              ->getRepository("FooBundle:User")
              ->findAll();

  $serializer = $this->container->get('jms_serializer');
  $jsonContent = $serializer
                   ->serialize(
                        $users, 
                        'json', 
                        SerializationContext::create()
                                 ->enableMaxDepthChecks()
                  );

  return new Response($jsonContent);
Run Code Online (Sandbox Code Playgroud)


Ani*_*nis 5

如果您使用的是Symfony 2.7 或更高版本,并且不想包含任何额外的用于序列化的包,也许您可​​以按照这种方式将学说实体序列化为 json -

  1. 在我的(公共的,父级)控制器中,我有一个准备序列化程序的函数

    use Symfony\Component\Serializer\Encoder\JsonEncoder;
    use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
    use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
    use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
    use Symfony\Component\Serializer\Serializer;
    
    // -----------------------------
    
    /**
     * @return Serializer
     */
    protected function _getSerializer()
    {  
        $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
        $normalizer           = new ObjectNormalizer($classMetadataFactory);
    
        return new Serializer([$normalizer], [new JsonEncoder()]);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 然后使用它来将实体序列化为 JSON

    $this->_getSerializer()->normalize($anEntity, 'json');
    $this->_getSerializer()->normalize($arrayOfEntities, 'json');
    
    Run Code Online (Sandbox Code Playgroud)

完毕!

但是您可能需要一些微调。例如 -