Ajouter des champs supplémentaires en utilisant le paquet Serializer de JMS

j'ai une entité que j'ai l'habitude de sérialiser en utilisant le paquet Serializer de JMS. Je dois ajouter à la sérialisation quelques champs qui ne résident pas dans l'entité elle-même mais qui sont rassemblés avec quelques requêtes de la base de données.

mon idée était de créer un objet personnalisé, remplir les champs avec les champs entity et ajouter le personnalisé. Mais cela semble un peu délicat et coûteux à faire pour chaque variation (j'utilise beaucoup de groupes de sérialisation) de la classe.

y a-t-il une meilleure façon de faire? cette? À l'aide d'une usine? Événements avant / après la sérialisation?

peut-être que je peux écouter pour la sérialisation et vérifier le type d'entité et les groupes de sérialisation ajouter les champs personnalisés? Mais au lieu de faire une requête pour chaque entité, il serait préférable de recueillir toutes les données de ces entités, puis l'ajouter à eux. Toute aide est appréciée

35
demandé sur alex88 2013-02-21 20:25:02

5 réponses

j'ai trouvé la solution toute seule,

pour ajouter un champ personnalisé après que la sérialisation a été faite, nous devons créer une classe d'auditeur comme ceci:

<?php

namespace Acme\DemoBundle\Listener;

use JMS\DiExtraBundle\Annotation\Service;
use JMS\DiExtraBundle\Annotation\Tag;
use JMS\DiExtraBundle\Annotation\Inject;
use JMS\DiExtraBundle\Annotation\InjectParams;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Acme\DemoBundle\Entity\Team;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\PreSerializeEvent;
use JMS\Serializer\EventDispatcher\ObjectEvent;
use JMS\Serializer\GraphNavigator;
use JMS\Serializer\JsonSerializationVisitor;

/**
 * Add data after serialization
 *
 * @Service("acme.listener.serializationlistener")
 * @Tag("jms_serializer.event_subscriber")
 */
class SerializationListener implements EventSubscriberInterface
{

    /**
     * @inheritdoc
     */
    static public function getSubscribedEvents()
    {
        return array(
            array('event' => 'serializer.post_serialize', 'class' => 'Acme\DemoBundle\Entity\Team', 'method' => 'onPostSerialize'),
        );
    }

    public function onPostSerialize(ObjectEvent $event)
    {
        $event->getVisitor()->addData('someKey','someValue');
    }
}

de Cette façon, vous pouvez ajouter des données à la sérialisé élément.

au lieu de cela, si vous voulez éditer un objet juste avant la sérialisation utilisez l'événement pre_serialize, soyez conscient que vous devez déjà avoir une variable (et les groupes de sérialisation corrects) si vous voulez utiliser pre_serialize pour ajouter un valeur.

69
répondu alex88 2013-06-03 09:17:36

je suis surpris que personne n'ait suggéré une façon beaucoup plus facile. Vous devez juste utiliser @VirtualProperty:

<?php

// ...
/**
 * @JMS\VirtualProperty
 * @JMS\SerializedName("someField")
 */
public function getSomeField()
{
    return $this->getTitle() . $this->getPromo();
}
13
répondu James Akwuh 2015-08-29 07:07:34

pour répondre à la question initiale. Voici comment limiter les données ajoutées pour certains groupes sérialisés (dans cet exemple some_data n'est ajouté que lorsque nous n'utilisons pas le list groupe:

public function onPostSerializeSomeEntityJson(ObjectEvent $event) {

    $entity = $event->getObject();

    if (!in_array('list', (array)$event->getContext()->attributes->get('groups'))) {

        $event->getVisitor()->addData('user_access', array(
            'some_data' => 'some_value'
        ));
    }
}

(array)$event->getContext()->attributes->get('groups') contient un tableau des groupes sérialisés utilisés.

9
répondu Petter Kjelkenes 2014-08-24 23:04:00

la réponse acceptée ne fonctionne que lorsque le visiteur est dérivé de \JMS\Serializer\GenericSerializationVisitor. Cela signifie que cela fonctionnera pour JSON, mais échouera pour XML.

voici un exemple de méthode qui s'adaptera au XML. Il examine les interfaces que l'objet visiteur supporte et agit de manière appropriée. Il montre comment vous pouvez ajouter un élément de lien aux objets JSON et XML sérialisés...

public function onPostSerialize(ObjectEvent $event)
{
    //obtain some data we want to add
    $link=array(
        'rel'=>'self',
        'href'=>'http://example.org/thing/1',
        'type'=>'application/thing+xml'
    );

    //see what our visitor supports...
    $visitor= $event->getVisitor();
    if ($visitor instanceof \JMS\Serializer\XmlSerializationVisitor)
    {
        //do XML things
        $doc=$visitor->getDocument();

        $element = $doc->createElement('link');
        foreach($link as $name => $value) {
            $element->setAttribute($name, $value);
        }
        $doc->documentElement->appendChild($element);
    } elseif ($visitor instanceof \JMS\Serializer\GenericSerializationVisitor)
    {
        $visitor->addData('link', $link);
    }


}
5
répondu Paul Dixon 2014-10-14 08:44:02

Qu'en ceci: http://jmsyst.com/libs/serializer/master/handlers

en résumé, vous définissez une classe qui reçoit un objet et renvoie du texte ou un tableau (qui sera converti en json).

vous avez la classe "IndexedStuff" qui contient un champ calculé bizarre qui pour une raison quelconque devrait être calculé au moment de la sérialisation.

<?php

namespace Project/Model;

class IndexedStuff
{
   public $name;
   public $value;
   public $rawData;
}

maintenant, créez le handler

<?php

namespace Project/Serializer;

use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\GraphNavigator;
use JMS\Serializer\JsonSerializationVisitor;
use JMS\Serializer\Context;

class MyHandler implements SubscribingHandlerInterface
{
    public function setEntityManager(Registry $registry) {
         // Inject registry instead of entity manager to avoid circular dependency
         $this->em = $registry->getEntityManager();
    }
    public static function getSubscribingMethods()
    {
        return array(
            array(
                'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
                'format' => 'json',
                'type' => 'Project/Model/IndexedStuff',
                'method' => 'serializeIndexedStuffToJson',
            ),
        );
    }

    public function serializeIndexedStuffToJson(JsonSerializationVisitor $visitor, Project/Model/IndexedStuff $stuff, array $type, Context $context)
    {
        // Build your object here and return it
        $score = $this->em->find("ProjectBundle:Calculator", $stuff->value)
        return array("score" => $score->getIndexScore(), "name"=> $score->name
    }
}

enfin enregistrer le service

services:
  project.serializer.stuff:
      class: Project\Serializer\MyHandler
      calls:
        - [setEntityManager, ["@doctrine"]]

maintenant partout où vous voulez sérialiser un objet de type "IndexedStuff" vous obtiendrez un json comme ceci

{"name": "myName", "score" => 0.3432}

de cette façon, vous pouvez personnaliser la façon dont votre entité est sérialisé

1
répondu Álvaro García 2016-06-13 12:15:42