可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm trying to update symfony2/doctrine entities using JMSSerializer with an @ExclusionPolicy:None @Groups Inclusion Policy.
* @Serializer\ExclusionPolicy("none") */ class Foo { /** * @Serializer\Groups({"flag","edit"}) */ protected $id; /** * @Serializer\Groups({"edit"}) */ protected $name; /** * @Serializer\Groups({"flag"}) */ protected $flag; /** * @Serializer\Exclude() */ protected $createdBy; }
reference: http://jmsyst.com/libs/serializer/master/reference/annotations
the result for the following record:
Foo (id:1, name:'bar', flagged:false ,created_by:123)
is serialized using Group inclusion to avoid serializing information I don't need (associations, blobs, etc..) so when I want to update an entity I deserialize only the updated fields of the entity from the JSON.
$foo->setFlagged(true); $data = $serializer->serialize($foo, 'json', SerializationContext::create()->setGroups(array("flag"))); result: {id:1,flagged:true}
which when passed back to the application deserializes into the entity
$foo = $serializer->deserialize($jsonFoo,'Foo','json'); result: Foo (id:1, name:null, flagged:true, created_by:null)
The problem is when I try to merge the entity back into the doctrine entity manager:
$foo = $em->merge($foo); $em->persist($foo); $em->flush();
The resulting foo is trying to update excluded properties (name,created_by) with null.
How do I tell JMSSerializer or Doctrine Entity Managers merge that I don't want to overwrite existing properties with null?
回答1:
I found the answer.
$serializer
is a service created by the symfony2 integration bundle JMSSerializerBundle
.
The default service is jms_serializer.serializer
initializes the JMSSerializer
with the default Object Constructor UnserializeObjectConstructor
and for doctrine I needed to deserialize with the DoctrineObjectConstructor
.
because I only use JMSSerializer
in the project for serialize/deserialize of doctrine entities, I overwrote JMSSerializerBundle
's jms_serializer.object_constructor
with the alias of the proper object constructor service.
<service id="jms_serializer.object_constructor" alias="jms_serializer.doctrine_object_constructor" public="false"/>
Is there a better way to configure what object constructor the serializer uses?
I also added the proper context to deserialize:
$serializer->deserialize($jsonFoo,'Foo','json', DeserializationContext::create()->setGroups(array('flag'))); result: Foo (id:1, name:'bar', flagged:true ,created_by:123)
Using the doctrine object constructor, it figures out that I want to find the object and only apply updates to fields provided in $jsonFoo
(and the flag group). This totally eliminates the need for doctrines entity manager merge and I can just persist the object properly.
$em->persist($foo); $em->flush();
回答2:
in addition to @Heyflynn's answer (thanks!), I needed this to work with doctrine_mongodb, so I modified my services.yml
as follows:
services: jms_serializer.doctrine_object_constructor: class: %jms_serializer.doctrine_object_constructor.class% public: false arguments: ["@doctrine_mongodb", "@jms_serializer.unserialize_object_constructor"] jms_serializer.object_constructor: alias: jms_serializer.doctrine_object_constructor
important fact is the @doctrine_mongodb
as argument for jms_serializer.doctrine_object_constructor
instead the original doctrine
parameter in the bundle's services.xml
:
<service id="jms_serializer.doctrine_object_constructor" class="%jms_serializer.doctrine_object_constructor.class%" public="false"> <argument type="service" id="doctrine"/> <argument type="service" id="jms_serializer.unserialize_object_constructor"/> </service> <service id="jms_serializer.unserialize_object_constructor" class="%jms_serializer.unserialize_object_constructor.class%" public="false" /> <service id="jms_serializer.object_constructor" alias="jms_serializer.unserialize_object_constructor" public="false" />
回答3:
To use JMS deserializer for MongoDB documents and ORM entities you can use
jms_serializer.doctrine_mongodb_object_constructor: class: %jms_serializer.doctrine_object_constructor.class% public: false arguments: ["@doctrine_mongodb", "@jms_serializer.unserialize_object_constructor"] jms_serializer.doctrine_object_constructor: class: %jms_serializer.doctrine_object_constructor.class% public: false arguments: ["@doctrine", "@jms_serializer.doctrine_mongodb_object_constructor"] jms_serializer.object_constructor: alias: jms_serializer.doctrine_object_constructor public: false
As you see in jms_serializer.doctrine_object_constructor
second argument (fallbackConstructor) is jms_serializer.doctrine_mongodb_object_constructor
it means that if your object isn't entity then jms will try to use fallbackConstructor and if your deserialised object isn't Document either then will be used default unserialize_object_constructor
if you deserialize entity
$em->persist($foo); $em->flush();
if document
$dm->persist($foo); $dm->flush();