Добавление дополнительных полей с помощью пакета сериализатора JMS

Я сущность, я обычно сериализовать объекты JMS сериализатор пакет. Я должен добавить в сериализацию некоторые поля, которые не находятся в самой сущности, но собраны с некоторыми запросами БД.

моя идея заключалась в том, чтобы создать пользовательский объект, заполнить поля с поля и добавить пользовательский. Но это кажется немного сложным и дорогостоящим для каждого варианта (я использую много групп сериализации) класса.

есть ли лучше/стандартный способ сделать это? Используя фабрику? События до / после сериализации?

может быть, я могу прослушать сериализацию и проверить тип сущности и группы сериализации добавить пользовательские поля? Но вместо того, чтобы делать запрос для каждой сущности, было бы лучше собрать все данные из смежных структур, а затем добавить к ним. Любая помощь приветствуется

5 ответов


Я нашел решение сам,

чтобы добавить настраиваемое поле после сериализации, мы должны создать класс прослушивателя следующим образом:

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

таким образом, вы можете добавить данные в сериализованный элемент.

вместо этого, если вы хотите изменить объект непосредственно перед сериализацией, используйте событие pre_serialize, имейте в виду, что вам уже нужно иметь переменную (и правильные группы сериализации), если вы хотите использовать pre_serialize для добавления значение.


меня удивляет, почему никто не предложил гораздо более простой способ. Вам нужно просто использовать @VirtualProperty:

<?php

// ...
/**
 * @JMS\VirtualProperty
 * @JMS\SerializedName("someField")
 */
public function getSomeField()
{
    return $this->getTitle() . $this->getPromo();
}

для дальнейшего ответа на исходный вопрос. Вот как вы ограничиваете добавленные данные для некоторых сериализованных групп (в этом примере some_data добавляется только тогда, когда мы не используем 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') содержит массив используемых сериализованных групп.


принятый ответ работает только тогда, когда посетитель получен из \JMS\Serializer\GenericSerializationVisitor. Это означает, что он будет работать для JSON, но не для XML.

вот пример метода, который будет справляться с XML. Он смотрит на интерфейсы, которые поддерживает объект посетителя, и действует соответствующим образом. Он показывает, как можно добавить элемент link в сериализованные объекты JSON и 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);
    }


}

как насчет этого: http://jmsyst.com/libs/serializer/master/handlers

В общем, вы определяете класс, который получает объект и возвращает текст или массив (который будет преобразован в json).

у вас есть класс "IndexedStuff", который содержит странное вычисляемое поле, которое по какой-то причине должно быть вычислено во время сериализации.

<?php

namespace Project/Model;

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

Теперь создайте обработчик

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

наконец-то зарегистрировать сервис

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

теперь везде, где вы хотите сериализовать объект типа "IndexedStuff", вы получите json, подобный этому

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

таким образом, вы можете полностью настроить, как ваша сущность сериализуется