40

我有一个实体,我通常使用 JMS Serializer 包进行序列化。我必须在序列化中添加一些不存在于实体本身但通过一些数据库查询收集的字段。

我的想法是创建一个自定义对象,用实体字段填充字段并添加自定义对象。但是对于类的每个变体(我使用很多序列化组),这似乎有点棘手和昂贵。

有没有更好/标准的方法来做到这一点?使用工厂?前/后序列化事件?

也许我可以监听序列化并检查实体类型和序列化组添加自定义字段?但是,与其对每个实体进行查询,不如收集相关实体的所有数据,然后将其添加到它们中。任何帮助表示赞赏

4

6 回答 6

72

我自己找到了解决方案,

要在序列化完成后添加自定义字段,我们必须创建一个监听器类,如下所示:

<?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 添加值,则需要已经有一个变量(和正确的序列化组)。

于 2013-05-21T17:05:57.997 回答
18

我很惊讶为什么没有人提出更简单的方法。你只需要使用@VirtualProperty:

<?php

// ...
/**
 * @JMS\VirtualProperty
 * @JMS\SerializedName("someField")
 */
public function getSomeField()
{
    return $this->getTitle() . $this->getPromo();
}
于 2015-08-29T07:07:34.343 回答
11

进一步回答原来的问题。以下是您如何限制某些序列化组的添加数据(在此示例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')包含使用的序列化组的数组。

于 2014-08-24T23:04:00.197 回答
6

接受的答案仅在访问者来自 时才有效\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);
    }


}
于 2014-10-14T08:44:02.857 回答
4

这个怎么样: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}

通过这种方式,您可以完全自定义实体的序列化方式

于 2016-06-13T12:15:42.923 回答
4

addData 自 2.0.0 以来已被弃用,因此我们需要这样做:

use JMS\Serializer\EventDispatcher\ObjectEvent;

class MySerializerHandler {

    public function onPostSerialize(ObjectEvent $event)
    {
        /** @var MySpecialObjectType $object */
        $myObject = $event->getObject();

        $key = 'customDataKey';
        $value = 'myvalue';

        $event->getVisitor()->visitProperty(
            new StaticPropertyMetadata('', $key, $value),
            $value
        );
    }
}

服务.yaml

services:
    MySerializerHandler:
        tags:
          - { name: jms_serializer.event_listener, class: 'MySpecialObjectType', event: serializer.post_serialize, method: 'onPostSerialize' }

https://github.com/schmittjoh/serializer/blob/c9c82c841b8ebe682ca44972d64fded215f72974/UPGRADING.md#from-1130-to-200

于 2020-03-03T04:50:42.490 回答