Agregar campos adicionales usando JMS Serializer bundle


Tengo una entidad que normalmente serializo usando el paquete Serializador JMS. Tengo que añadir a la serialización de algunos campos que no residen en la misma entidad, pero se reunió con algunas consultas db.

Mi idea era crear un objeto personalizado, rellenar los campos con los campos de entidad y añadir el personalizado. Pero esto parece un poco complicado y costoso de hacer para cada variación (uso muchos grupos de serialización) de la clase.

¿Hay una forma mejor/estándar de hacer esto? Usando un fábrica? ¿Eventos de serialización Pre / Post?

Tal vez pueda escuchar la serialización y comprobar el tipo de entidad y los grupos de serialización agregar los campos personalizados? Pero en lugar de hacer una consulta para cada entidad, sería mejor recopilar todos los datos de las entidades relacionadas y luego agregarlos a ellas. Cualquier ayuda es apreciada

Author: alex88, 2013-02-21

5 answers

He encontrado la solución por mí mismo,

Para agregar un campo personalizado después de que se haya hecho la serialización, tenemos que crear una clase de escucha como esta:

<?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 esta manera puede agregar datos al elemento serializado.

En su lugar, si desea editar un objeto justo antes de la serialización use el evento pre_serialize, tenga en cuenta que ya necesita tener una variable (y los grupos de serialización correctos) si desea usar pre_serialize para agregar un valor.

 69
Author: alex88,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-03 09:17:36

Me sorprende que nadie haya sugerido una manera mucho más fácil. Solo necesitas usar @VirtualProperty:

<?php

// ...
/**
 * @JMS\VirtualProperty
 * @JMS\SerializedName("someField")
 */
public function getSomeField()
{
    return $this->getTitle() . $this->getPromo();
}
 13
Author: James Akwuh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-29 07:07:34

Para responder más a la pregunta original. Así es como limita los datos agregados para algunos grupos serializados (en este ejemplo, some_data solo se agrega cuando no estamos usando el grupo list:

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') contiene una matriz de los grupos serializados utilizados.

 9
Author: Petter Kjelkenes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-24 23:04:00

La respuesta aceptada solo funciona cuando el visitante se deriva de \JMS\Serializer\GenericSerializationVisitor. Esto significa que funcionará para JSON, pero fallará para XML.

Aquí hay un método de ejemplo que hará frente a XML. Observa las interfaces que soporta el objeto visitante y actúa apropiadamente. Muestra cómo puede agregar un elemento de enlace a objetos serializados JSON y XML...

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
Author: Paul Dixon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-14 08:44:02

Qué hay de esto: http://jmsyst.com/libs/serializer/master/handlers

En resumen, se define una clase que recibe un objeto y devuelve texto o una matriz (que se convertirá a json).

Usted tiene la clase "IndexedStuff" que contiene un extraño campo calculado que por alguna razón debe ser calculado en tiempo de serialización.

<?php

namespace Project/Model;

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

Ahora crea el controlador

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

Finalmente registrar el servicio

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

Ahora donde quieras serializar un objeto de tipo "IndexedStuff" obtendrá un json como este

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

De esta manera puede personalizar completamente cómo se serializa su entidad

 1
Author: Álvaro García,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-13 12:15:42