सिस्कोनी 2.0 AJAX आवेदन में JSON को Doctrine संस्थाओं को कैसे एनकोड करना है?


89

मैं गेम ऐप विकसित कर रहा हूं और सिम्फनी 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साइकिल चलाने से बचने के लिए ..,

जवाबों:


82

एक अन्य विकल्प JMSSerializerBundle का उपयोग करना है । अपने नियंत्रक में आप तब करते हैं

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

7
आपको JMS \ SerializerBundle \ Annotation \ ExclusionPolicy का उपयोग करने की आवश्यकता है ; JMS \ SerializerBundle \ Annotation \ Exclude का उपयोग करें; अपनी इकाई में और काम करने के लिए JMSSerializerBundle स्थापित करें
ioleo

3
यदि आप इसे बदलते हैं तो बहुत अच्छा काम करता है: नई प्रतिक्रिया ($ रिपोर्ट) लौटाएं;
ग्रेविवर

7
चूंकि एनोटेशन बंडल से बाहर ले जाया गया है, इसलिए सही उपयोग स्टेटमेंट अब हैं: JMS \ Serializer \ Annotation \ ExclusionPolicy का उपयोग करें; JMS \ Serializer \ Annotation \ Exclude का उपयोग करें;
पियर-ल्यूक गेंड्रेउ

3
डॉक्ट्रिन के लिए दस्तावेज़ीकरण में कहा गया है कि वस्तुओं को क्रमबद्ध न करें या बहुत सावधानी से क्रमबद्ध न करें।
ब्लूबरन

मुझे JMSSerializerBundle को स्थापित करने की भी आवश्यकता नहीं थी। आपका कोड JMSSerializerBundle की आवश्यकता के बिना काम किया।
डर्क जान स्पिलमैन

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

और फिर कॉल करें

json_encode(MyUserEntity);

1
मुझे यह घोल बहुत पसंद है!
माइकल

3
यह एक बढ़िया समाधान ... यदि आप कम से कम अन्य बंडलों पर अपनी निर्भरता रखने के लिए प्रयास कर रहे हैं है
Drmjo

5
लिंक्ड संस्थाओं के बारे में क्या?
जॉन द रिपर

7
यह इकाई संग्रह (यानी: OneToManyसंबंध) के साथ काम करने के लिए प्रतीत नहीं होता है
पियरे डे लेसपिन

1
यह एकल जिम्मेदारी सिद्धांत का उल्लंघन करता है और यह अच्छा नहीं है यदि आपकी संस्थाएं सिद्धांत द्वारा उत्पन्न ऑटो हैं
jim smith

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

3
धन्यवाद, लेकिन मेरे पास प्लेयर एंटिटी है, जिसमें गेम एंटिटीज़ कलेक्शन का लिंक है और हर गेम यूनिट के पास ऐसे खिलाड़ियों के लिंक हैं जो इसमें खेले हैं। कुछ इस तरह। और क्या आपको लगता है कि GetSetMethodNormalizer सही ढंग से काम करेगा (यह पुनरावर्ती एल्गोरिदम का उपयोग करता है)?
Dmytro Krasun

2
हाँ यह पुनरावर्ती है और मेरे मामले में यह मेरी समस्या थी। इसलिए, विशिष्ट संस्थाओं के लिए, आप CustomNormalizer और इसके NormalizableInterface का उपयोग कर सकते हैं जैसा कि आप जानते हैं।
webda2l

2
जब मैंने इसकी कोशिश की, तो मुझे "घातक त्रुटि: 134217728 बाइट्स की स्मृति आकार समाप्त हो गई (64 बाइट्स आवंटित करने की कोशिश की गई) /home/jason/pressbox/vendor/symfony/src/Symfony/Component/Serializer/GetSetMethodNormalfinizer में। लाइन 44 ”। मुझे आश्चर्य है क्योंकि?
जेसन स्वेट

1
जब मैंने कोशिश की तो मैं अपवाद से नीचे आ गया .. घातक त्रुटि: '100' के अधिकतम फ़ंक्शन नेस्टिंग स्तर पर पहुंच गया, गर्भपात! सी में: \ wamp \ www \ myapp \ Application \ पुस्तकालयों \ doctrine \ Symfony \ घटक \ Serializer \ Normalizer \ GetSetMethodNormalizer.php लाइन 223 पर
user2350664


11

जवाब पूरा करने के लिए: Symfony2 json_encode के आसपास एक आवरण के साथ आता है: Symfony / Component / HttpFoundation / JsonResponse

अपने नियंत्रकों में विशिष्ट उपयोग:

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

उम्मीद है की यह मदद करेगा

जे


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 }

मेरे नियंत्रक में:

$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 में सरणियों deserialize करने के लिए इसे कॉन्फ़िगर कर सकते हैं


3
Symfony 2.3+ में Serializer घटक का उपयोग करने के बारे में एक रसोई की किताब प्रविष्टि है, जैसा कि आप अब अंतर्निहित एक को सक्रिय कर सकते हैं: symfony.com/doc/current/cookbook/serializer.html
althaus

6

मुझे बस एक ही समस्या को हल करना था: json-एन्कोडिंग एक इकाई ("उपयोगकर्ता") जिसमें एक-से-कई द्विदिश एसोसिएशन एक दूसरे इकाई ("स्थान") के लिए हैं।

मैंने कई चीजों की कोशिश की और मुझे लगता है कि अब मुझे सबसे अच्छा स्वीकार्य समाधान मिल गया है। यह विचार डेविड द्वारा लिखित समान कोड का उपयोग करने के लिए था, लेकिन किसी तरह नॉर्मलाइज़र को किसी बिंदु पर रोकने के लिए कहकर अनंत पुनरावृत्ति को रोक देता है।

मैं एक कस्टम नॉर्मलाइज़र को लागू नहीं करना चाहता था, क्योंकि यह गेटसेमेथोडेनरलाइज़र मेरी राय में एक अच्छा दृष्टिकोण है (प्रतिबिंब आदि के आधार पर)। इसलिए मैंने इसे कम करने का फैसला किया है, जो पहली नजर में मामूली नहीं है, क्योंकि किसी संपत्ति (isGetMethod) को शामिल करने का तरीका निजी है।

लेकिन, कोई सामान्य विधि को ओवरराइड कर सकता है, इसलिए मैंने इस बिंदु पर अवरोधन किया, बस "स्थान" का संदर्भ देने वाली संपत्ति को परेशान करते हुए - इसलिए अपर्याप्त लूप बाधित होता है।

कोड में यह इस तरह दिखता है:

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

} 

1
मुझे आश्चर्य है कि इसे सामान्य करना कितना आसान होगा, ताकि 1. कभी भी इकाई वर्गों को छूने की आवश्यकता न हो, 2. न केवल "स्थान" को रिक्त करें, बल्कि प्रत्येक संग्रह प्रकार फ़ील्ड जो अन्य एंटाइट्स के लिए संभवतः मैप करता है। यानी Ent की कोई आंतरिक / अग्रिम जानकारी उसे क्रमबद्ध करने के लिए आवश्यक नहीं, पुनरावर्तन-मुक्त।
मार्कोस

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

एक नॉर्मलाइज़र का एक उदाहरण:

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


6

सिम्फनी में 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 के साथ समस्याएं बनी रहेंगी।


6

यह अधिक अद्यतन है (सिम्फनी 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);

5

यदि आप सिम्फनी 2.7 या इसके बाद के संस्करण का उपयोग कर रहे हैं , और क्रमांकन के लिए कोई अतिरिक्त बंडल शामिल नहीं करना चाहते हैं, तो हो सकता है कि आप इस तरह के सिद्धांत का पालन कर सकते हैं जो ईश्वर के लिए सिद्धांतों को जब्त कर सकते हैं -

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

किया हुआ!

लेकिन आपको कुछ ठीक ट्यूनिंग की आवश्यकता हो सकती है। उदाहरण के लिए -


4

जब आपको सिम्फनी पर बहुत सारे REST एपीआई एंडपॉइंट्स बनाने की आवश्यकता होती है, तो बंडल के निम्नलिखित स्टैक का उपयोग करने का सबसे अच्छा तरीका है:

  1. JMSSerializerBundle सिद्धांत सिद्धांतों के क्रमांकन के लिए
  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 () ब्राउज़र से सीधे समापन बिंदु का परीक्षण करने की अनुमति देता है, बिना किसी अन्य क्लाइंट या जावास्क्रिप्ट कोड के

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.