diff --git a/Serializer/GenericDeserializationVisitor.php b/Serializer/GenericDeserializationVisitor.php
index 1f151805..07bb8bd6 100644
--- a/Serializer/GenericDeserializationVisitor.php
+++ b/Serializer/GenericDeserializationVisitor.php
@@ -164,7 +164,7 @@ public function visitProperty(PropertyMetadata $metadata, $data)
{
$name = $this->namingStrategy->translateName($metadata);
- if ( ! isset($data[$name])) {
+ if ( ! array_key_exists($name, $data)) {
return;
}
@@ -172,9 +172,14 @@ public function visitProperty(PropertyMetadata $metadata, $data)
throw new RuntimeException(sprintf('You must define a type for %s::$%s.', $metadata->reflection->class, $metadata->name));
}
- $v = $this->navigator->accept($data[$name], $metadata->type, $this);
- if (null === $v) {
- return;
+ $v = null;
+
+ if ($data[$name] !== null) {
+ $v = $this->navigator->accept($data[$name], $metadata->type, $this);
+
+ if (null === $v) {
+ return;
+ }
}
if (null === $metadata->setter) {
diff --git a/Serializer/GenericSerializationVisitor.php b/Serializer/GenericSerializationVisitor.php
index 19026cbf..9de51ae7 100644
--- a/Serializer/GenericSerializationVisitor.php
+++ b/Serializer/GenericSerializationVisitor.php
@@ -134,7 +134,10 @@ public function visitProperty(PropertyMetadata $metadata, $data)
$v = (null === $metadata->getter ? $metadata->reflection->getValue($data)
: $data->{$metadata->getter}());
- $v = $this->navigator->accept($v, $metadata->type, $this);
+ if ($v !== null) {
+ $v = $this->navigator->accept($v, $metadata->type, $this);
+ }
+
if (null === $v && !$this->shouldSerializeNull()) {
return;
}
diff --git a/Serializer/GraphNavigator.php b/Serializer/GraphNavigator.php
index 937453d5..128070e3 100644
--- a/Serializer/GraphNavigator.php
+++ b/Serializer/GraphNavigator.php
@@ -129,6 +129,7 @@ public function accept($data, array $type = null, VisitorInterface $visitor)
return $visitor->visitBoolean($data, $type);
case 'double':
+ case 'float':
return $visitor->visitDouble($data, $type);
case 'array':
@@ -240,7 +241,7 @@ public function detachObject($object)
{
if (null === $object) {
throw new InvalidArgumentException('$object cannot be null');
- } else if (!is_object($object)) {
+ } elseif (!is_object($object)) {
throw new InvalidArgumentException(sprintf('Expected an object to detach, given "%s".', gettype($object)));
}
diff --git a/Serializer/Handler/DateTimeHandler.php b/Serializer/Handler/DateTimeHandler.php
index a81dcabf..987ccc2f 100644
--- a/Serializer/Handler/DateTimeHandler.php
+++ b/Serializer/Handler/DateTimeHandler.php
@@ -60,48 +60,66 @@ public function __construct($defaultFormat = \DateTime::ISO8601, $defaultTimezon
$this->defaultTimezone = new \DateTimeZone($defaultTimezone);
}
- public function serializeDateTimeToXml(XmlSerializationVisitor $visitor, \DateTime $date, array $type)
+ public function serializeDateTimeToXml(XmlSerializationVisitor $visitor, $date, array $type)
{
- if (null === $visitor->document) {
- $visitor->document = $visitor->createDocument(null, null, true);
- }
-
- return $visitor->document->createTextNode($date->format($this->getFormat($type)));
+ if ($date === null) {
+ return $visitor->visitNull(null, $type);
+ }
+
+ return $visitor->visitString($date->format($this->getFormat($type)), $type);
}
- public function serializeDateTimeToYml(YamlSerializationVisitor $visitor, \DateTime $date, array $type)
+ public function serializeDateTimeToYml(YamlSerializationVisitor $visitor, $date, array $type)
{
- return Inline::dump($date->format($this->getFormat($type)));
+ if ($date === null) {
+ return $visitor->visitNull(null, $type);
+ }
+
+ return $visitor->visitString($date->format($this->getFormat($type)), $type);
}
- public function serializeDateTimeToJson(JsonSerializationVisitor $visitor, \DateTime $date, array $type)
+ public function serializeDateTimeToJson(JsonSerializationVisitor $visitor, $date, array $type)
{
- return $date->format($this->getFormat($type));
+ if ($date === null) {
+ return $visitor->visitNull(null, $type);
+ }
+
+ return $visitor->visitString($date->format($this->getFormat($type)), $type);
}
public function deserializeDateTimeFromXml(XmlDeserializationVisitor $visitor, $data, array $type)
{
+ $attributes = $data->attributes();
+
+ if (isset($attributes['nil'][0]) && (string) $attributes['nil'][0] === 'true') {
+ return null;
+ }
+
return $this->parseDateTime($data, $type);
}
public function deserializeDateTimeFromJson(JsonDeserializationVisitor $visitor, $data, array $type)
{
+ if ($data === null) {
+ return null;
+ }
+
return $this->parseDateTime($data, $type);
}
private function parseDateTime($data, array $type)
{
$timezone = isset($type['params'][1]) ? $type['params'][1] : $this->defaultTimezone;
- $datetime = \DateTime::createFromFormat($this->getFormat($type), (string) $data, $timezone);
- if (false === $datetime) {
- throw new RuntimeException(sprintf('Invalid datetime "%s", expected format %s.', $data, $this->defaultFormat));
+ $datetime = \DateTime::createFromFormat($this->getFormat($type), (string) $data, $timezone);
+ if (false === $datetime) {
+ throw new RuntimeException(sprintf('Invalid datetime "%s", expected format %s.', $data, $this->defaultFormat));
}
- return $datetime;
+ return $datetime;
}
private function getFormat(array $type)
{
return isset($type['params'][0]) ? $type['params'][0] : $this->defaultFormat;
}
-}
\ No newline at end of file
+}
diff --git a/Serializer/YamlSerializationVisitor.php b/Serializer/YamlSerializationVisitor.php
index 71cba87e..e5bc7dcd 100644
--- a/Serializer/YamlSerializationVisitor.php
+++ b/Serializer/YamlSerializationVisitor.php
@@ -173,18 +173,21 @@ public function visitProperty(PropertyMetadata $metadata, $data)
$count = $this->writer->changeCount;
- if (null !== $v = $this->navigator->accept($v, $metadata->type, $this)) {
+ if ($v === null) {
+ $this->writer->rtrim(false)->writeln(' null');
+ } elseif (null !== $v = $this->navigator->accept($v, $metadata->type, $this)) {
$this->writer
->rtrim(false)
->writeln(' '.$v)
;
- } else if ($count === $this->writer->changeCount) {
+ } elseif ($count === $this->writer->changeCount) {
$this->writer->revert();
}
if (!$metadata->inline) {
$this->writer->outdent();
}
+
$this->revertCurrentMetadata();
}
@@ -212,4 +215,4 @@ public function getResult()
{
return $this->writer->getContent();
}
-}
\ No newline at end of file
+}
diff --git a/Tests/Fixtures/InitializedObjectConstructor.php b/Tests/Fixtures/InitializedObjectConstructor.php
new file mode 100644
index 00000000..3f4d1f56
--- /dev/null
+++ b/Tests/Fixtures/InitializedObjectConstructor.php
@@ -0,0 +1,45 @@
+
+ *
+ * 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\SerializerBundle\Tests\Fixtures;
+
+use Doctrine\Common\Collections\ArrayCollection;
+
+use JMS\SerializerBundle\Metadata\ClassMetadata;
+
+use JMS\SerializerBundle\Serializer\Construction\ObjectConstructorInterface;
+use JMS\SerializerBundle\Serializer\VisitorInterface;
+
+use JMS\SerializerBundle\Tests\Fixtures\Author;
+
+class InitializedObjectConstructor implements ObjectConstructorInterface
+{
+ public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type)
+ {
+ $post = new \StdClass;
+ $post->title = 'This is a nice title.';
+ $post->author = new Author('Foo Bar');
+ $post->createdAt = new \DateTime('2011-07-30 00:00', new \DateTimeZone('UTC'));
+ $post->comments = new ArrayCollection();
+ $post->published = false;
+
+ $post->comments->add(new \StdClass);
+
+ return $post;
+ }
+}
diff --git a/Tests/Serializer/BaseSerializationTest.php b/Tests/Serializer/BaseSerializationTest.php
index a4387e4a..87ea4c15 100644
--- a/Tests/Serializer/BaseSerializationTest.php
+++ b/Tests/Serializer/BaseSerializationTest.php
@@ -60,6 +60,7 @@
use JMS\SerializerBundle\Tests\Fixtures\GroupsObject;
use JMS\SerializerBundle\Tests\Fixtures\IndexedCommentsBlogPost;
use JMS\SerializerBundle\Tests\Fixtures\InlineParent;
+use JMS\SerializerBundle\Tests\Fixtures\InitializedObjectConstructor;
use JMS\SerializerBundle\Tests\Fixtures\Log;
use JMS\SerializerBundle\Tests\Fixtures\ObjectWithLifecycleCallbacks;
use JMS\SerializerBundle\Tests\Fixtures\ObjectWithVersionedVirtualProperties;
@@ -106,15 +107,30 @@ public function testSerializeNullObject()
$this->serializer->setSerializeNull(false);
}
- public function testNull()
+ /**
+ * @dataProvider getTypes
+ */
+ public function testNull($type)
{
- $this->assertEquals($this->getContent('null'), $this->serialize(null));
+ $this->assertEquals($this->getContent('null'), $this->serialize(null), $type);
if ($this->hasDeserializer()) {
- $this->assertEquals(null, $this->deserialize($this->getContent('null'), 'NULL'));
+ $this->assertEquals(null, $this->deserialize($this->getContent('null'), $type));
}
}
+ public function getTypes()
+ {
+ return array(
+ array('NULL'),
+ array('integer'),
+ array('double'),
+ array('float'),
+ array('string'),
+ array('DateTime'),
+ );
+ }
+
public function testString()
{
$this->assertEquals($this->getContent('string'), $this->serialize('foo'));
@@ -144,21 +160,23 @@ public function getBooleans()
/**
* @dataProvider getNumerics
*/
- public function testNumerics($key, $value)
+ public function testNumerics($key, $value, $type)
{
$this->assertEquals($this->getContent($key), $this->serialize($value));
if ($this->hasDeserializer()) {
- $this->assertEquals($value, $this->deserialize($this->getContent($key), is_double($value) ? 'double' : 'integer'));
+ $this->assertEquals($value, $this->deserialize($this->getContent($key), $type));
}
}
public function getNumerics()
{
return array(
- array('integer', 1),
- array('float', 4.533),
- array('float_trailing_zero', 1.0),
+ array('integer', 1, 'integer'),
+ array('float', 4.533, 'double'),
+ array('float', 4.533, 'float'),
+ array('float_trailing_zero', 1.0, 'double'),
+ array('float_trailing_zero', 1.0, 'float'),
);
}
@@ -226,6 +244,29 @@ public function testArrayMixed()
$this->assertEquals($this->getContent('array_mixed'), $this->serialize(array('foo', 1, true, new SimpleObject('foo', 'bar'), array(1, 3, true))));
}
+ /**
+ * @dataProvider getDateTime
+ */
+ public function testDateTime($key, $value, $type)
+ {
+ $this->assertEquals($this->getContent($key), $this->serialize($value, $type));
+
+ if ($this->hasDeserializer()) {
+ $deserialized = $this->deserialize($this->getContent($key), $type);
+
+ $this->assertTrue(is_object($deserialized));
+ $this->assertEquals(get_class($value), get_class($deserialized));
+ $this->assertEquals($value->getTimestamp(), $deserialized->getTimestamp());
+ }
+ }
+
+ public function getDateTime()
+ {
+ return array(
+ array('date_time', new \DateTime('2011-08-30 00:00', new \DateTimeZone('UTC')), 'DateTime'),
+ );
+ }
+
public function testBlogPost()
{
$post = new BlogPost('This is a nice title.', $author = new Author('Foo Bar'), new \DateTime('2011-07-30 00:00', new \DateTimeZone('UTC')));
@@ -243,6 +284,32 @@ public function testBlogPost()
}
}
+ public function testDeserializingNull()
+ {
+ if (get_class($this) === 'JMS\SerializerBundle\Tests\Serializer\XmlSerializationTest') {
+ $this->markTestSkipped('Deserializing null not working in XML.');
+ }
+
+ $objectConstructor = new InitializedObjectConstructor();
+ $this->serializer = new Serializer($this->factory, $this->handlerRegistry, $objectConstructor, $this->dispatcher, null, $this->serializationVisitors, $this->deserializationVisitors);
+ $this->serializer->setSerializeNull(true);
+
+ $post = new BlogPost('This is a nice title.', $author = new Author('Foo Bar'), new \DateTime('2011-07-30 00:00', new \DateTimeZone('UTC')));
+
+ $this->setField($post, 'author', null);
+
+ $this->assertEquals($this->getContent('blog_post_unauthored'), $this->serialize($post));
+
+ if ($this->hasDeserializer()) {
+ $deserialized = $this->deserialize($this->getContent('blog_post_unauthored'), get_class($post));
+ $this->assertEquals('2011-07-30T00:00:00+0000', $this->getField($deserialized, 'createdAt')->format(\DateTime::ISO8601));
+ $this->assertAttributeEquals('This is a nice title.', 'title', $deserialized);
+ $this->assertAttributeSame(false, 'published', $deserialized);
+ $this->assertAttributeEquals(new ArrayCollection(), 'comments', $deserialized);
+ $this->assertEquals(null, $this->getField($deserialized, 'author'));
+ }
+ }
+
public function testReadOnly()
{
$author = new AuthorReadOnly(123, 'Ruud Kamphuis');
diff --git a/Tests/Serializer/JsonSerializationTest.php b/Tests/Serializer/JsonSerializationTest.php
index aa0e2e14..76e21519 100644
--- a/Tests/Serializer/JsonSerializationTest.php
+++ b/Tests/Serializer/JsonSerializationTest.php
@@ -18,21 +18,16 @@
namespace JMS\SerializerBundle\Tests\Serializer;
-use JMS\SerializerBundle\Serializer\VisitorInterface;
-
-use JMS\SerializerBundle\Serializer\GraphNavigator;
-
-use JMS\SerializerBundle\Serializer\EventDispatcher\EventSubscriberInterface;
+use JMS\SerializerBundle\Exception\RuntimeException;
use JMS\SerializerBundle\Serializer\EventDispatcher\Event;
+use JMS\SerializerBundle\Serializer\EventDispatcher\EventSubscriberInterface;
+use JMS\SerializerBundle\Serializer\GraphNavigator;
+use JMS\SerializerBundle\Serializer\VisitorInterface;
use JMS\SerializerBundle\Tests\Fixtures\Author;
-
use JMS\SerializerBundle\Tests\Fixtures\AuthorList;
-use JMS\SerializerBundle\Exception\RuntimeException;
-use JMS\SerializerBundle\Tests\Fixtures\SimpleObject;
-
class JsonSerializationTest extends BaseSerializationTest
{
protected function getContent($key)
@@ -56,6 +51,7 @@ protected function getContent($key)
$outputs['array_objects'] = '[{"foo":"foo","moo":"bar","camel_case":"boo"},{"foo":"baz","moo":"boo","camel_case":"boo"}]';
$outputs['array_mixed'] = '["foo",1,true,{"foo":"foo","moo":"bar","camel_case":"boo"},[1,3,true]]';
$outputs['blog_post'] = '{"title":"This is a nice title.","created_at":"2011-07-30T00:00:00+0000","is_published":false,"comments":[{"author":{"full_name":"Foo Bar"},"text":"foo"}],"author":{"full_name":"Foo Bar"}}';
+ $outputs['blog_post_unauthored'] = '{"title":"This is a nice title.","created_at":"2011-07-30T00:00:00+0000","is_published":false,"comments":[],"author":null}';
$outputs['price'] = '{"price":3}';
$outputs['currency_aware_price'] = '{"currency":"EUR","amount":2.34}';
$outputs['order'] = '{"cost":{"price":12.34}}';
@@ -86,6 +82,7 @@ protected function getContent($key)
$outputs['simple_object_nullable'] = '{"foo":"foo","moo":"bar","camel_case":"boo","null_property":null}';
$outputs['input'] = '{"attributes":{"type":"text","name":"firstname","value":"Adrien"}}';
$outputs['hash_empty'] = '{"hash":{}}';
+ $outputs['date_time'] = '"2011-08-30T00:00:00+0000"';
}
if (!isset($outputs[$key])) {
diff --git a/Tests/Serializer/xml/blog_post_unauthored.xml b/Tests/Serializer/xml/blog_post_unauthored.xml
new file mode 100644
index 00000000..6a07ea08
--- /dev/null
+++ b/Tests/Serializer/xml/blog_post_unauthored.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/Tests/Serializer/xml/date_time.xml b/Tests/Serializer/xml/date_time.xml
new file mode 100644
index 00000000..00156d46
--- /dev/null
+++ b/Tests/Serializer/xml/date_time.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/Tests/Serializer/yml/blog_post_unauthored.yml b/Tests/Serializer/yml/blog_post_unauthored.yml
new file mode 100644
index 00000000..bf2887b5
--- /dev/null
+++ b/Tests/Serializer/yml/blog_post_unauthored.yml
@@ -0,0 +1,4 @@
+title: 'This is a nice title.'
+created_at: '2011-07-30T00:00:00+0000'
+is_published: false
+author: null
diff --git a/Tests/Serializer/yml/date_time.yml b/Tests/Serializer/yml/date_time.yml
new file mode 100644
index 00000000..a8f98ee4
--- /dev/null
+++ b/Tests/Serializer/yml/date_time.yml
@@ -0,0 +1 @@
+'2011-08-30T00:00:00+0000'