我有一个实体,我通常使用JMS Serializer包进行序列化。我必须在序列化中添加一些字段,这些字段不驻留在实体本身中,而是通过一些数据库查询收集。
我的想法是创建一个自定义对象,用实体字段填充字段并添加自定义对象。但是对于每个变种(我使用很多序列化组)而言,这似乎有点棘手且昂贵。
有更好/标准的方法吗?用工厂?前/后序列化事件?
也许我可以监听序列化并检查实体类型和序列化组添加自定义字段?但是,不是对每个实体进行查询,而是收集相关实体的所有数据然后将其添加到它们中更好。任何帮助表示赞赏
答案 0 :(得分:69)
我自己找到了解决方案,
在序列化完成后添加自定义字段我们要创建一个这样的监听器类:
<?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来添加值,则需要已经有一个变量(以及正确的序列化组)。 p>
答案 1 :(得分:14)
我很惊讶为什么没有人提出更简单的方法。你只需要使用@VirtualProperty:
<?php
// ...
/**
* @JMS\VirtualProperty
* @JMS\SerializedName("someField")
*/
public function getSomeField()
{
return $this->getTitle() . $this->getPromo();
}
答案 2 :(得分:10)
进一步回答原来的问题。以下是限制某些序列化组的添加数据的方法(在此示例中,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')
包含已使用的序列化组的数组。
答案 3 :(得分:5)
只有当访问者来自\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);
}
}
答案 4 :(得分:2)
这个怎么样: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}
通过这种方式,您可以完全自定义实体的序列化方式
答案 5 :(得分:2)
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
);
}
}
services.yaml
services:
MySerializerHandler:
tags:
- { name: jms_serializer.event_listener, class: 'MySpecialObjectType', event: serializer.post_serialize, method: 'onPostSerialize' }