瀏覽代碼

Allow deserialization to constructed objects

Pass DeserializationContext to object constructors.
Eugene Dounar 11 年之前
父節點
當前提交
99d5ceeb04

+ 8 - 7
src/JMS/Serializer/Construction/DoctrineObjectConstructor.php

@@ -2,13 +2,13 @@
 
 /*
  * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *     http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -21,6 +21,7 @@ namespace JMS\Serializer\Construction;
 use Doctrine\Common\Persistence\ManagerRegistry;
 use JMS\Serializer\VisitorInterface;
 use JMS\Serializer\Metadata\ClassMetadata;
+use JMS\Serializer\DeserializationContext;
 
 /**
  * Doctrine object constructor for new (or existing) objects during deserialization.
@@ -45,14 +46,14 @@ class DoctrineObjectConstructor implements ObjectConstructorInterface
     /**
      * {@inheritdoc}
      */
-    public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type)
+    public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type, DeserializationContext $context)
     {
         // Locate possible ObjectManager
         $objectManager = $this->managerRegistry->getManagerForClass($metadata->name);
 
         if (!$objectManager) {
             // No ObjectManager found, proceed with normal deserialization
-            return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type);
+            return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
         }
 
         // Locate possible ClassMetadata
@@ -60,7 +61,7 @@ class DoctrineObjectConstructor implements ObjectConstructorInterface
 
         if ($classMetadataFactory->isTransient($metadata->name)) {
             // No ClassMetadata found, proceed with normal deserialization
-            return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type);
+            return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
         }
 
         // Managed entity, check for proxy load
@@ -75,7 +76,7 @@ class DoctrineObjectConstructor implements ObjectConstructorInterface
 
         foreach ($classMetadata->getIdentifierFieldNames() as $name) {
             if ( ! array_key_exists($name, $data)) {
-                return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type);
+                return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
             }
 
             $identifierList[$name] = $data[$name];

+ 5 - 4
src/JMS/Serializer/Construction/ObjectConstructorInterface.php

@@ -2,13 +2,13 @@
 
 /*
  * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *     http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,6 +20,7 @@ namespace JMS\Serializer\Construction;
 
 use JMS\Serializer\VisitorInterface;
 use JMS\Serializer\Metadata\ClassMetadata;
+use JMS\Serializer\DeserializationContext;
 
 /**
  * Implementations of this interface construct new objects during deserialization.
@@ -41,5 +42,5 @@ interface ObjectConstructorInterface
      *
      * @return object
      */
-    public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type);
+    public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type, DeserializationContext $context);
 }

+ 5 - 4
src/JMS/Serializer/Construction/UnserializeObjectConstructor.php

@@ -2,13 +2,13 @@
 
 /*
  * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *     http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,10 +20,11 @@ namespace JMS\Serializer\Construction;
 
 use JMS\Serializer\VisitorInterface;
 use JMS\Serializer\Metadata\ClassMetadata;
+use JMS\Serializer\DeserializationContext;
 
 class UnserializeObjectConstructor implements ObjectConstructorInterface
 {
-    public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type)
+    public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type, DeserializationContext $context)
     {
         return unserialize(sprintf('O:%d:"%s":0:{}', strlen($metadata->name), $metadata->name));
     }

+ 4 - 4
src/JMS/Serializer/GraphNavigator.php

@@ -2,13 +2,13 @@
 
 /*
  * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *     http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -200,7 +200,7 @@ final class GraphNavigator
 
                 $object = $data;
                 if ($context instanceof DeserializationContext) {
-                    $object = $this->objectConstructor->construct($visitor, $metadata, $data, $type);
+                    $object = $this->objectConstructor->construct($visitor, $metadata, $data, $type, $context);
                 }
 
                 if (isset($metadata->handlerCallbacks[$context->getDirection()][$context->getFormat()])) {

+ 42 - 0
tests/JMS/Serializer/Tests/Fixtures/InitializedBlogPostConstructor.php

@@ -0,0 +1,42 @@
+<?php
+
+/*
+ * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace JMS\Serializer\Tests\Fixtures;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+use JMS\Serializer\Metadata\ClassMetadata;
+use JMS\Serializer\DeserializationContext;
+
+use JMS\Serializer\Construction\ObjectConstructorInterface;
+use JMS\Serializer\VisitorInterface;
+
+use JMS\Serializer\Tests\Fixtures\Author;
+use JMS\Serializer\Construction\UnserializeObjectConstructor;
+
+class InitializedBlogPostConstructor extends UnserializeObjectConstructor
+{
+    public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type, DeserializationContext $context)
+    {
+        if ($type['name'] !== 'JMS\Serializer\Tests\Fixtures\BlogPost') {
+            return parent::construct($visitor, $metadata, $data, $type);
+        }
+
+        return new BlogPost('This is a nice title.', new Author('Foo Bar'), new \DateTime('2011-07-30 00:00', new \DateTimeZone('UTC')));
+    }
+}

+ 30 - 15
tests/JMS/Serializer/Tests/Fixtures/InitializedObjectConstructor.php

@@ -2,13 +2,13 @@
 
 /*
  * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *     http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,24 +18,39 @@
 
 namespace JMS\Serializer\Tests\Fixtures;
 
-use Doctrine\Common\Collections\ArrayCollection;
-
+use JMS\Serializer\VisitorInterface;
 use JMS\Serializer\Metadata\ClassMetadata;
-
+use JMS\Serializer\DeserializationContext;
 use JMS\Serializer\Construction\ObjectConstructorInterface;
-use JMS\Serializer\VisitorInterface;
-
-use JMS\Serializer\Tests\Fixtures\Author;
-use JMS\Serializer\Construction\UnserializeObjectConstructor;
 
-class InitializedObjectConstructor extends UnserializeObjectConstructor
+/**
+ * Object constructor that allows deserialization into already constructed
+ * objects passed through the deserialization context
+ */
+class InitializedObjectConstructor implements ObjectConstructorInterface
 {
-    public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type)
+    private $fallbackConstructor;
+
+    /**
+     * Constructor.
+     *
+     * @param ObjectConstructorInterface $fallbackConstructor Fallback object constructor
+     */
+    public function __construct(ObjectConstructorInterface $fallbackConstructor)
     {
-        if ($type['name'] !== 'JMS\Serializer\Tests\Fixtures\BlogPost') {
-            return parent::construct($visitor, $metadata, $data, $type);
+        $this->fallbackConstructor = $fallbackConstructor;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type, DeserializationContext $context)
+    {
+        if ($context->attributes->containsKey('target') && $context->getDepth() === 1) {
+            return $context->attributes->get('target')->get();
         }
 
-        return new BlogPost('This is a nice title.', new Author('Foo Bar'), new \DateTime('2011-07-30 00:00', new \DateTimeZone('UTC')));
+        return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
     }
+
 }

+ 31 - 1
tests/JMS/Serializer/Tests/Serializer/BaseSerializationTest.php

@@ -66,6 +66,7 @@ use JMS\Serializer\Tests\Fixtures\InvalidGroupsObject;
 use JMS\Serializer\Tests\Fixtures\IndexedCommentsBlogPost;
 use JMS\Serializer\Tests\Fixtures\InlineParent;
 use JMS\Serializer\Tests\Fixtures\InitializedObjectConstructor;
+use JMS\Serializer\Tests\Fixtures\InitializedBlogPostConstructor;
 use JMS\Serializer\Tests\Fixtures\Log;
 use JMS\Serializer\Tests\Fixtures\ObjectWithLifecycleCallbacks;
 use JMS\Serializer\Tests\Fixtures\ObjectWithVersionedVirtualProperties;
@@ -306,7 +307,7 @@ abstract class BaseSerializationTest extends \PHPUnit_Framework_TestCase
 
     public function testDeserializingNull()
     {
-        $objectConstructor = new InitializedObjectConstructor();
+        $objectConstructor = new InitializedBlogPostConstructor();
         $this->serializer = new Serializer($this->factory, $this->handlerRegistry, $objectConstructor, $this->serializationVisitors, $this->deserializationVisitors, $this->dispatcher);
 
         $post = new BlogPost('This is a nice title.', $author = new Author('Foo Bar'), new \DateTime('2011-07-30 00:00', new \DateTimeZone('UTC')));
@@ -734,6 +735,35 @@ abstract class BaseSerializationTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($this->getContent('tree'), $this->serializer->serialize($data, $this->getFormat(), $context));
     }
 
+    public function testDeserializingIntoExistingObject()
+    {
+        if (!$this->hasDeserializer()) {
+            return;
+        }
+
+        $objectConstructor = new InitializedObjectConstructor(new UnserializeObjectConstructor());
+        $serializer = new Serializer(
+            $this->factory, $this->handlerRegistry, $objectConstructor,
+            $this->serializationVisitors, $this->deserializationVisitors, $this->dispatcher
+        );
+
+        $order = new Order(new Price(12));
+
+        $context = new DeserializationContext();
+        $context->attributes->set('target', $order);
+
+        $deseralizedOrder = $serializer->deserialize(
+            $this->getContent('order'),
+            get_class($order),
+            $this->getFormat(),
+            $context
+        );
+
+        $this->assertSame($order, $deseralizedOrder);
+        $this->assertEquals(new Order(new Price(12.34)), $deseralizedOrder);
+        $this->assertAttributeInstanceOf('JMS\Serializer\Tests\Fixtures\Price', 'cost', $deseralizedOrder);
+    }
+
     abstract protected function getContent($key);
     abstract protected function getFormat();