bestsource

Symfony 2.0 AJAX 어플리케이션에서 JSON으로 인코딩하는 방법

bestsource 2023. 4. 4. 21:51
반응형

Symfony 2.0 AJAX 어플리케이션에서 JSON으로 인코딩하는 방법

게임 앱을 개발하고 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');
    }
}

모든 컨트롤러는 엔티티를 취득하여 필드의 일부를 JSON으로 인코딩합니다.노멀라이저를 사용하여 모든 권한을 인코딩할 수 있습니다.그러나 엔티티가 다른 엔티티에 대한 링크를 순환시킨 경우에는 어떻게 해야 할까요?또는 엔티티 그래프가 매우 큰가요?제안할 것이 있습니까?

엔티티에 대한 인코딩 스키마를 생각해 봤는데... 「」를 사용합니다.NormalizableInterface 사사 , ,,,,,,,,..,

이제 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,
        );
    }
}

그리고 전화한다.

json_encode(MyUserEntity);

다른 옵션은 JMS SerializerBundle을 사용하는 것입니다.컨트롤러에서는 다음 작업을 수행할 수 있습니다.

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

엔티티 클래스의 주석을 사용하여 직렬화가 수행되는 방법을 구성할 수 있습니다.위 링크의 매뉴얼을 참조하십시오.예를 들어 링크된 엔티티를 제외하는 방법은 다음과 같습니다.

 /**
* 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;

다음을 사용하여 복잡한 엔티티인 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');

답변을 완료하려면 Symfony2는 json_encode 주위에 래퍼가 붙어 있습니다.Symfony / 컴포넌트 / Http Foundation / Json Response

컨트롤러의 일반적인 사용 방법:

...
use Symfony\Component\HttpFoundation\JsonResponse;
...
public function acmeAction() {
...
return new JsonResponse($array);
}

엔티티 시리얼화 문제에 대한 해결책은 다음과 같습니다.

#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 }

내 컨트롤러:

$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));

기타 예:

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

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

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

return new Response($json);

http://api.symfony.com/2.0에서 어레이를 역직렬화하도록 구성할 수도 있습니다.

같은 문제를 해결해야 했습니다.1 대 다의 쌍방향 어소시에이션을 가지는 엔티티(「사용자」)를 다른 엔티티(「Location」)로 인코딩하는 것입니다.

나는 몇 가지 시도를 했고, 나는 지금 가장 수용 가능한 해결책을 찾았다고 생각한다.그 아이디어는 David이 쓴 것과 같은 코드를 사용하는 것이지만, 어떤 식으로든 Normalizer에게 어느 시점에서 멈추라고 함으로써 무한 재귀를 가로채는 것이었다.

이 GetSetMethodNormalizer는 (반사 등에 근거한) 훌륭한 접근법이기 때문에 커스텀 노멀라이저를 실장하고 싶지 않았습니다.속성(isGetMethod)을 포함시킬지 여부를 나타내는 방법은 비공개이기 때문에 언뜻 보기에는 사소한 것이 아닌 서브클래스를 정했습니다.

그러나 정규화 메서드를 덮어쓸 수 있기 때문에 이 시점에서 단순히 "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);
    }

} 

저도 같은 문제가 있어서 재귀에 대처할 수 있는 인코더를 만들기로 했습니다.

는 이 수업을 하는 수업을 .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);
}

Normalizer의 예:

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.)
    );
}

}

컨트롤러 내:

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

완전한 코드는, https://github.com/progracqteur/WikiPedale/tree/master/src/Progracqteur/WikipedaleBundle/Resources/Normalizer 에 있습니다.

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 }

및 컨트롤러의 예:

/**
 * Поиск сущности по ИД объекта и ИД языка
 * @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 );
}

그러나 필드 유형 \DateTime의 문제는 해결되지 않습니다.

는 더 많은 업데이트입니다(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
      ......................
      ......................
   */

액션 내부:

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);

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()]);
    }
    
  2. 그런 다음 엔티티를 JSON으로 직렬화하는 데 사용합니다.

    $this->_getSerializer()->normalize($anEntity, 'json');
    $this->_getSerializer()->normalize($arrayOfEntities, 'json');
    

알았어!

하지만 미세 조정이 필요할 수도 있습니다.예:

Symfony에서 많은 REST API 엔드포인트를 생성해야 하는 경우 다음 번들 스택을 사용하는 것이 가장 좋습니다.

  1. JMS SerializerBundle(독트린 엔티티 시리얼라이제이션용)
  2. 응답 뷰 리스너용 FOSRestBundle 번들또, 컨트롤러/액션명에 근거해 루트의 정의를 생성할 수도 있습니다.
  3. NelmioApiDocBundle을 통해 온라인 문서 및 샌드박스 자동 생성(외부 도구 없이 엔드포인트 테스트 가능)

모든 것을 올바르게 설정하면 엔티티 코드는 다음과 같습니다.

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;

/**
 * @ORM\Table(name="company")
 */
class Company
{

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     *
     * @JMS\Expose()
     * @JMS\SerializedName("name")
     * @JMS\Groups({"company_overview"})
     */
    private $name;

    /**
     * @var Campaign[]
     *
     * @ORM\OneToMany(targetEntity="Campaign", mappedBy="company")
     * 
     * @JMS\Expose()
     * @JMS\SerializedName("campaigns")
     * @JMS\Groups({"campaign_overview"})
     */
    private $campaigns;
}

다음으로 컨트롤러의 코드:

use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use FOS\RestBundle\Controller\Annotations\View;

class CompanyController extends Controller
{

    /**
     * Retrieve all companies
     *
     * @View(serializerGroups={"company_overview"})
     * @ApiDoc()
     *
     * @return Company[]
     */
    public function cgetAction()
    {
        return $this->getDoctrine()->getRepository(Company::class)->findAll();
    }
}

이러한 설정의 이점은 다음과 같습니다.

  • @JMS\Expose()엔티티의 주석을 단순 필드 및 모든 유형의 관계에 추가할 수 있습니다.실행의 사용)가.@JMS\VirtualProperty()에) ★★★★★★★★★★★★★」
  • 시리얼화 그룹을 사용하면, 다양한 상황에서 노출된 필드를 제어할 수 있습니다.
  • 컨트롤러는 매우 간단합니다.액션 메서드는 엔티티 또는 엔티티의 배열을 직접 반환할 수 있으며 이러한 엔티티는 자동으로 직렬화됩니다.
  • 그리고.@ApiDoc()REST 클라이언트 또는 JavaScript 코드 없이 브라우저에서 직접 엔드포인트를 테스트할 수 있습니다.

또한 Acrin ORM Transformations를 사용하여 엔티티를 네스트된 스칼라 배열로 변환하여

승인된 답변은 맞지만 엔티티의 필터링된 서브셋을 시리얼화해야 하는 경우 json_encode로 충분합니다.

다음 예를 생각해 보겠습니다.

class FileTypeRepository extends ServiceEntityRepository  
{

   const ALIAS = 'ft';
   const SHORT_LIST = 'ft.name name';

    public function __construct(ManagerRegistry $registry)
    {
         parent::__construct($registry, FileType::class);
    }

    public function getAllJsonFileTypes() 
    {
        return json_encode($this->getAllFileTypes());
    } 

   /**
    * @return array
    */
    public function getAllFileTypes()
    {
        $query = $this->createQueryBuilder(self::ALIAS);
        $query->select(self::SHORT_LIST);
        return $query->getQuery()->getResult();
    }
}



/** THIS IS ENOUGH TO SERIALIZE AN ARRAY OF ENTITIES SINCE the doctrine SELECT will remove complex data structures from the entities itself **/
json_encode($this->getAllFileTypes()); 

짧은 메모:Symfony 5.1 이상에서 테스트 완료

언급URL : https://stackoverflow.com/questions/6706485/how-to-encode-doctrine-entities-to-json-in-symfony-2-0-ajax-application

반응형