bug #33733 [Serializer] fix denormalization of string-arrays with only one element (mkrauser)

This PR was merged into the 3.4 branch.

Discussion
----------

[Serializer] fix denormalization of string-arrays with only one element

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  |no
| Deprecations? |no
| Tickets       | Fix #33731
| License       | MIT
| Doc PR        |

This PR does almost the same as ac70edf8cd, just not only for arrays of objects.

Commits
-------

8814751b96 [Serializer] fix denormalization of string-arrays with only one element #33731
This commit is contained in:
Nicolas Grekas 2019-09-30 16:55:04 +02:00
commit a2cd56c12f
2 changed files with 63 additions and 17 deletions

View File

@ -244,16 +244,18 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
return null; 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; $builtinType = Type::BUILTIN_TYPE_OBJECT;
$class = $collectionValueType->getClassName().'[]'; $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()) { if (null !== $collectionKeyType = $type->getCollectionKeyType()) {
$context['key_type'] = $collectionKeyType; $context['key_type'] = $collectionKeyType;
} }

View File

@ -121,16 +121,54 @@ class AbstractObjectNormalizerTest extends TestCase
$extractor = $this->getMockBuilder(PhpDocExtractor::class)->getMock(); $extractor = $this->getMockBuilder(PhpDocExtractor::class)->getMock();
$extractor->method('getTypes') $extractor->method('getTypes')
->will($this->onConsecutiveCalls( ->will($this->onConsecutiveCalls(
[ [new Type('array', false, null, true, new Type('int'), new Type('object', false, DummyChild::class))],
new Type( null
'array', ));
false,
null, $denormalizer = new AbstractObjectNormalizerCollectionDummy(null, null, $extractor);
true, $arrayDenormalizer = new ArrayDenormalizerDummy();
new Type('int'), $serializer = new SerializerCollectionDummy([$arrayDenormalizer, $denormalizer]);
new Type('object', false, DummyChild::class) $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 null
)); ));
@ -212,6 +250,12 @@ class AbstractObjectNormalizerWithMetadata extends AbstractObjectNormalizer
} }
} }
class StringCollection
{
/** @var string[] */
public $children;
}
class DummyCollection class DummyCollection
{ {
/** @var DummyChild[] */ /** @var DummyChild[] */