带有抽象 class 的 JMSSerializerBundle - Symfony2
JMSSerializerBundle with abstract class - Symfony2
我想序列化和反序列化一个实体及其依赖项,但我无法序列化涉及抽象的元素 class。
层次结构:
测试 --> 几个 Calls
其中 Call
class 是一个抽象 class 并由 TestCallExecuteQuery
扩展(与 [=21 相同的问题=])
Test.php :
/**
* @ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
* @ORM\Table(name="cfa_test")
* @JMSSer\ExclusionPolicy("all")
*/
class Test
{
/**
* @ORM\OneToMany(targetEntity="TestCall", mappedBy="test", cascade={"all"}, orphanRemoval=true)
* @JMSSer\Expose
* @JMSSer\Groups({"export"})
* @JMSSer\Type("ArrayCollection<App\Bundle\CapFileAnalyzerBundle\Entity\TestCall>")
*/
protected $calls;
/**
* @ORM\OneToMany(targetEntity="TestCondition", mappedBy="test", cascade={"all"}, orphanRemoval=true)
* @JMSSer\Expose
* @JMSSer\Groups({"export"})
* @JMSSer\Type("ArrayCollection<App\Bundle\CapFileAnalyzerBundle\Entity\TestCondition>")
*/
protected $conditions;
TestCall.php :
/**
* @ORM\Entity
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\Table(name="cfa_test_call")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({
* "executeQuery" = "App\Bundle\CapFileAnalyzerBundle\Entity\TestCallExecuteQuery",
* "call" = "App\Bundle\CapFileAnalyzerBundle\Entity\TestCall"
* })
* @JMSSer\ExclusionPolicy("all")
* @JMSSer\Discriminator(field="serializedType", map={
* "executeQuery"="App\Bundle\CapFileAnalyzerBundle\Entity\TestCallExecuteQuery",
* "call" = "App\Bundle\CapFileAnalyzerBundle\Entity\TestCall"
* })
*/
abstract class TestCall
{
/**
* @JMSSer\Expose
* @JMSSer\Groups({"export"})
*/
protected $type = 'call';
/**
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\ManyToOne(targetEntity="Test", inversedBy="calls")
*/
protected $test;
/**
* @JMSSer\VirtualProperty()
* @JMSSer\SerializedName("serializedType")
*/
public function getDiscr()
{
return $this->type;
}
TestCallExecuteQuery.php :
/**
* @ORM\Entity
* @JMSSer\ExclusionPolicy("all")
*/
class TestCallExecuteQuery extends TestCall
{
protected $type = 'executeQuery';
/**
* @ORM\Column(name="`query`", type="text")
* @JMSSer\Expose
* @JMSSer\Groups({"export"})
*/
protected $query;
/**
* @ORM\Column(name="`return`", type="string", nullable=true)
* @JMSSer\Expose
* @JMSSer\Groups({"export"})
*/
protected $return;
所以我按照在互联网上找到的说明进行操作:
@JMSSer\Expose
每个 class 中带有 @JMSSer\ExclusionPolicy("all")
的注释
@JMSSer\Discriminator
抽象之上的注释 class TestCall
与扩展器映射 class (TestcallExecuteQuery
)
但是.. 当我序列化时,我只得到类型 属性 的 TestCall 而不是 query
或 return
属性 在 TestCallExecuteQuery
中定义:
{"tests":[{"calls":[{"type":"executeQuery"},{"type":"executeQuery"}], ... }
我知道这是可能的,因为我得到过一次,但即使让时钟倒转我也无法重现..
{"tests":[{"calls":[{"query":"SELECT * FROM table","return":"return_1"}], ... }
编辑:
好的,我可能通过更改 Test.php
得到了 query
和 return
:
/**
* @JMSSer\Type("ArrayCollection<App\Bundle\CapFileAnalyzerBundle\Entity\TestCall>")
*/
protected $calls;
收件人:
/**
* @JMSSer\Type("ArrayCollection<App\Bundle\CapFileAnalyzerBundle\Entity\TestCallExecuteQuery>")
*/
protected $calls;
我做错了什么?
好的!在几天失去理智后,我找到了解决方案!
解决方案是创建两个事件侦听器 PreSerialize 和 PostSerialize。
首先我在TestCall.php
(摘要Class)中删除了这部分:
/**
* @JMSSer\VirtualProperty()
* @JMSSer\SerializedName("serializedType")
*/
public function getDiscr()
{
return $this->type;
}
并在 TestCallExecuteQuery.php
(扩展程序 class)中添加了这些注释:
/**
* @JMSSer\Type("string")
* @JMSSer\Expose
* @JMSSer\Groups({"export"})
*/
protected $type = 'executeQuery';
我的听众看起来像这样:
<?php
namespace App\Bundle\CapFileAnalyzerBundle\EventListener;
use JMS\Serializer\EventDispatcher\Events;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\ObjectEvent;
use JMS\Serializer\EventDispatcher\PreSerializeEvent;
class JMSSerializerListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
['event' => Events::PRE_SERIALIZE, 'method' => 'onPreSerialize'],
['event' => Events::POST_SERIALIZE, 'method' => 'onPostSerialize']
];
}
/**
* @param PreSerializeEvent $event
*/
public function onPreSerialize(PreSerializeEvent $event)
{
$object = $event->getObject();
if (is_object($object) &&
is_subclass_of($object, 'App\Bundle\CapFileAnalyzerBundle\Entity\TestCall') &&
get_class($object) !== $event->getType()['name']
) {
$event->setType(get_class($event->getObject()));
}
}
/**
* @param ObjectEvent $event
*/
public function onPostSerialize(ObjectEvent $event){
$object = $event->getObject();
if (is_object($object) &&
is_a($object, 'App\Bundle\CapFileAnalyzerBundle\Entity\TestCallExecuteQuery')) {
$event->getVisitor()->addData("serializedType", $object->getType());
}
}
}
监听器声明:
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="cfa.events.jmsserializer_listener.class">App\Bundle\CapFileAnalyzerBundle\EventListener\JMSSerializerListener</parameter>
</parameters>
<services>
<service id="cfa.events.jmsserializer_listener" class="%cfa.events.jmsserializer_listener.class%">
<tag name="jms_serializer.event_subscriber"/>
</service>
</services>
</container>
我稍微解释一下:
- PreSerailize 事件
如果要序列化的 object 是我的摘要 class 的子 class(在我的例子中是 TestCall
),我必须强制事件类型 object 被序列化到相关的 subclass (在我的例子中是 TestCallExecuteQuery
)。事实上,正确的object(TestCallExecuteQuery
)被传递但是它被映射到它的parentclass(摘要classTestCall
)
转储 $event
object :
PreSerializeEvent {#977 ▼
-object: TestCallExecuteQuery {#981 ▼
#type: "executeQuery"
#query: "SELECT * FROM table_name"
#return: "return_3"
#id: 2
#test: Test {#948 ▶}
}
#type: array:2 [▼
"name" => "App\Bundle\CapFileAnalyzerBundle\Entity\TestCall"
"params" => []
]
-context: SerializationContext {#420 ▶}
}
- 连载后事件
If serialized object is my child class I add a visitor 属性 (not a virtual even custom) ..
注意:JMSSerializeBundle 不会序列化 "virtual" 属性 直接添加到 class,例如通过这样的方法:
public function createProperty($name, $value) {
$this->{$name} = $value;
}
也许 Discriminator/Virtual 属性 像 JMSSerializerBundle 中那样被添加,这就是为什么它们没有被序列化。真的不知道。
我想序列化和反序列化一个实体及其依赖项,但我无法序列化涉及抽象的元素 class。
层次结构:
测试 --> 几个 Calls
其中 Call
class 是一个抽象 class 并由 TestCallExecuteQuery
扩展(与 [=21 相同的问题=])
Test.php :
/**
* @ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
* @ORM\Table(name="cfa_test")
* @JMSSer\ExclusionPolicy("all")
*/
class Test
{
/**
* @ORM\OneToMany(targetEntity="TestCall", mappedBy="test", cascade={"all"}, orphanRemoval=true)
* @JMSSer\Expose
* @JMSSer\Groups({"export"})
* @JMSSer\Type("ArrayCollection<App\Bundle\CapFileAnalyzerBundle\Entity\TestCall>")
*/
protected $calls;
/**
* @ORM\OneToMany(targetEntity="TestCondition", mappedBy="test", cascade={"all"}, orphanRemoval=true)
* @JMSSer\Expose
* @JMSSer\Groups({"export"})
* @JMSSer\Type("ArrayCollection<App\Bundle\CapFileAnalyzerBundle\Entity\TestCondition>")
*/
protected $conditions;
TestCall.php :
/**
* @ORM\Entity
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\Table(name="cfa_test_call")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({
* "executeQuery" = "App\Bundle\CapFileAnalyzerBundle\Entity\TestCallExecuteQuery",
* "call" = "App\Bundle\CapFileAnalyzerBundle\Entity\TestCall"
* })
* @JMSSer\ExclusionPolicy("all")
* @JMSSer\Discriminator(field="serializedType", map={
* "executeQuery"="App\Bundle\CapFileAnalyzerBundle\Entity\TestCallExecuteQuery",
* "call" = "App\Bundle\CapFileAnalyzerBundle\Entity\TestCall"
* })
*/
abstract class TestCall
{
/**
* @JMSSer\Expose
* @JMSSer\Groups({"export"})
*/
protected $type = 'call';
/**
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\ManyToOne(targetEntity="Test", inversedBy="calls")
*/
protected $test;
/**
* @JMSSer\VirtualProperty()
* @JMSSer\SerializedName("serializedType")
*/
public function getDiscr()
{
return $this->type;
}
TestCallExecuteQuery.php :
/**
* @ORM\Entity
* @JMSSer\ExclusionPolicy("all")
*/
class TestCallExecuteQuery extends TestCall
{
protected $type = 'executeQuery';
/**
* @ORM\Column(name="`query`", type="text")
* @JMSSer\Expose
* @JMSSer\Groups({"export"})
*/
protected $query;
/**
* @ORM\Column(name="`return`", type="string", nullable=true)
* @JMSSer\Expose
* @JMSSer\Groups({"export"})
*/
protected $return;
所以我按照在互联网上找到的说明进行操作:
@JMSSer\Expose
每个 class 中带有 @JMSSer\Discriminator
抽象之上的注释 classTestCall
与扩展器映射 class (TestcallExecuteQuery
)
@JMSSer\ExclusionPolicy("all")
的注释
但是.. 当我序列化时,我只得到类型 属性 的 TestCall 而不是 query
或 return
属性 在 TestCallExecuteQuery
中定义:
{"tests":[{"calls":[{"type":"executeQuery"},{"type":"executeQuery"}], ... }
我知道这是可能的,因为我得到过一次,但即使让时钟倒转我也无法重现..
{"tests":[{"calls":[{"query":"SELECT * FROM table","return":"return_1"}], ... }
编辑:
好的,我可能通过更改 Test.php
得到了 query
和 return
:
/**
* @JMSSer\Type("ArrayCollection<App\Bundle\CapFileAnalyzerBundle\Entity\TestCall>")
*/
protected $calls;
收件人:
/**
* @JMSSer\Type("ArrayCollection<App\Bundle\CapFileAnalyzerBundle\Entity\TestCallExecuteQuery>")
*/
protected $calls;
我做错了什么?
好的!在几天失去理智后,我找到了解决方案!
解决方案是创建两个事件侦听器 PreSerialize 和 PostSerialize。
首先我在TestCall.php
(摘要Class)中删除了这部分:
/**
* @JMSSer\VirtualProperty()
* @JMSSer\SerializedName("serializedType")
*/
public function getDiscr()
{
return $this->type;
}
并在 TestCallExecuteQuery.php
(扩展程序 class)中添加了这些注释:
/**
* @JMSSer\Type("string")
* @JMSSer\Expose
* @JMSSer\Groups({"export"})
*/
protected $type = 'executeQuery';
我的听众看起来像这样:
<?php
namespace App\Bundle\CapFileAnalyzerBundle\EventListener;
use JMS\Serializer\EventDispatcher\Events;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\ObjectEvent;
use JMS\Serializer\EventDispatcher\PreSerializeEvent;
class JMSSerializerListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
['event' => Events::PRE_SERIALIZE, 'method' => 'onPreSerialize'],
['event' => Events::POST_SERIALIZE, 'method' => 'onPostSerialize']
];
}
/**
* @param PreSerializeEvent $event
*/
public function onPreSerialize(PreSerializeEvent $event)
{
$object = $event->getObject();
if (is_object($object) &&
is_subclass_of($object, 'App\Bundle\CapFileAnalyzerBundle\Entity\TestCall') &&
get_class($object) !== $event->getType()['name']
) {
$event->setType(get_class($event->getObject()));
}
}
/**
* @param ObjectEvent $event
*/
public function onPostSerialize(ObjectEvent $event){
$object = $event->getObject();
if (is_object($object) &&
is_a($object, 'App\Bundle\CapFileAnalyzerBundle\Entity\TestCallExecuteQuery')) {
$event->getVisitor()->addData("serializedType", $object->getType());
}
}
}
监听器声明:
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="cfa.events.jmsserializer_listener.class">App\Bundle\CapFileAnalyzerBundle\EventListener\JMSSerializerListener</parameter>
</parameters>
<services>
<service id="cfa.events.jmsserializer_listener" class="%cfa.events.jmsserializer_listener.class%">
<tag name="jms_serializer.event_subscriber"/>
</service>
</services>
</container>
我稍微解释一下:
- PreSerailize 事件
如果要序列化的 object 是我的摘要 class 的子 class(在我的例子中是 TestCall
),我必须强制事件类型 object 被序列化到相关的 subclass (在我的例子中是 TestCallExecuteQuery
)。事实上,正确的object(TestCallExecuteQuery
)被传递但是它被映射到它的parentclass(摘要classTestCall
)
转储 $event
object :
PreSerializeEvent {#977 ▼
-object: TestCallExecuteQuery {#981 ▼
#type: "executeQuery"
#query: "SELECT * FROM table_name"
#return: "return_3"
#id: 2
#test: Test {#948 ▶}
}
#type: array:2 [▼
"name" => "App\Bundle\CapFileAnalyzerBundle\Entity\TestCall"
"params" => []
]
-context: SerializationContext {#420 ▶}
}
- 连载后事件
If serialized object is my child class I add a visitor 属性 (not a virtual even custom) ..
注意:JMSSerializeBundle 不会序列化 "virtual" 属性 直接添加到 class,例如通过这样的方法:
public function createProperty($name, $value) {
$this->{$name} = $value;
}
也许 Discriminator/Virtual 属性 像 JMSSerializerBundle 中那样被添加,这就是为什么它们没有被序列化。真的不知道。