Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

Я нашел решение самостоятельно,

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

<?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 для добавления значения.

Ответ 2

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

<?php

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

Ответ 3

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

Ответ 4

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

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


}

Ответ 5

Как насчет этого: 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}

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