From 8814751b960d5c75b4d6ebb23548c7c938c2d65d Mon Sep 17 00:00:00 2001 From: Matthias Krauser Date: Fri, 27 Sep 2019 11:43:15 +0200 Subject: [PATCH] [Serializer] fix denormalization of string-arrays with only one element #33731 --- .../Normalizer/AbstractObjectNormalizer.php | 16 +++-- .../AbstractObjectNormalizerTest.php | 64 ++++++++++++++++--- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index f2fb082b4b..6cb6ec5013 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -244,16 +244,18 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer return null; } - if ($type->isCollection() && null !== ($collectionValueType = $type->getCollectionValueType()) && Type::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()) { + $collectionValueType = $type->isCollection() ? $type->getCollectionValueType() : null; + + // Fix a collection that contains the only one element + // This is special to xml format only + if ('xml' === $format && null !== $collectionValueType && (!\is_array($data) || !\is_int(key($data)))) { + $data = [$data]; + } + + if (null !== $collectionValueType && Type::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()) { $builtinType = Type::BUILTIN_TYPE_OBJECT; $class = $collectionValueType->getClassName().'[]'; - // Fix a collection that contains the only one element - // This is special to xml format only - if ('xml' === $format && !\is_int(key($data))) { - $data = [$data]; - } - if (null !== $collectionKeyType = $type->getCollectionKeyType()) { $context['key_type'] = $collectionKeyType; } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index f1eafe811f..4895526c45 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -121,16 +121,54 @@ class AbstractObjectNormalizerTest extends TestCase $extractor = $this->getMockBuilder(PhpDocExtractor::class)->getMock(); $extractor->method('getTypes') ->will($this->onConsecutiveCalls( - [ - new Type( - 'array', - false, - null, - true, - new Type('int'), - new Type('object', false, DummyChild::class) - ), - ], + [new Type('array', false, null, true, new Type('int'), new Type('object', false, DummyChild::class))], + null + )); + + $denormalizer = new AbstractObjectNormalizerCollectionDummy(null, null, $extractor); + $arrayDenormalizer = new ArrayDenormalizerDummy(); + $serializer = new SerializerCollectionDummy([$arrayDenormalizer, $denormalizer]); + $arrayDenormalizer->setSerializer($serializer); + $denormalizer->setSerializer($serializer); + + return $denormalizer; + } + + public function testDenormalizeStringCollectionDecodedFromXmlWithOneChild() + { + $denormalizer = $this->getDenormalizerForStringCollection(); + + // if an xml-node can have children which should be deserialized as string[] + // and only one child exists + $stringCollection = $denormalizer->denormalize(['children' => 'foo'], StringCollection::class, 'xml'); + + $this->assertInstanceOf(StringCollection::class, $stringCollection); + $this->assertIsArray($stringCollection->children); + $this->assertCount(1, $stringCollection->children); + $this->assertEquals('foo', $stringCollection->children[0]); + } + + public function testDenormalizeStringCollectionDecodedFromXmlWithTwoChildren() + { + $denormalizer = $this->getDenormalizerForStringCollection(); + + // if an xml-node can have children which should be deserialized as string[] + // and only one child exists + $stringCollection = $denormalizer->denormalize(['children' => ['foo', 'bar']], StringCollection::class, 'xml'); + + $this->assertInstanceOf(StringCollection::class, $stringCollection); + $this->assertIsArray($stringCollection->children); + $this->assertCount(2, $stringCollection->children); + $this->assertEquals('foo', $stringCollection->children[0]); + $this->assertEquals('bar', $stringCollection->children[1]); + } + + private function getDenormalizerForStringCollection() + { + $extractor = $this->getMockBuilder(PhpDocExtractor::class)->getMock(); + $extractor->method('getTypes') + ->will($this->onConsecutiveCalls( + [new Type('array', false, null, true, new Type('int'), new Type('string'))], null )); @@ -212,6 +250,12 @@ class AbstractObjectNormalizerWithMetadata extends AbstractObjectNormalizer } } +class StringCollection +{ + /** @var string[] */ + public $children; +} + class DummyCollection { /** @var DummyChild[] */