feature #32832 [Serializer] Allow multi-dimenstion object array in AbstractObjectNormalizer (alediator)

This PR was submitted for the 4.3 branch but it was squashed and merged into the 4.4 branch instead (closes #32832).

Discussion
----------

[Serializer] Allow multi-dimenstion object array in AbstractObjectNormalizer

Modify ´AbstractObjectNormalizer´ adding the capability of parsing nested arrays of objects instead of parsing them into arrays

| Q             | A
| ------------- | ---
| Branch?       | 4.3
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #31175
| License       | MIT

I was trying to parse a nested array of objects and I'd find out with this #31175 behavior. After trying to solve in different ways, I was able to do it adding the fix that allows the `AbstractObjectNormalizer` to parse more than one dimension array of objects.

I would like to add some tests to assure it works and doesn't generate any side effects, but I'd prefer to open the PR to link it to the existing issue https://github.com/symfony/symfony/issues/31175.

On the other hand, if you think it is not applicable or you want me to change anything, please let me know.

Commits
-------

ea03f6d664 [Serializer] Allow multi-dimenstion object array in AbstractObjectNormalizer
This commit is contained in:
Fabien Potencier 2019-09-08 09:15:48 +02:00
commit 6916822e46
2 changed files with 98 additions and 0 deletions

View File

@ -415,6 +415,26 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
if (null !== $collectionKeyType = $type->getCollectionKeyType()) {
$context['key_type'] = $collectionKeyType;
}
} elseif ($type->isCollection() && null !== ($collectionValueType = $type->getCollectionValueType()) && Type::BUILTIN_TYPE_ARRAY === $collectionValueType->getBuiltinType()) {
// get inner type for any nested array
$innerType = $collectionValueType;
// note that it will break for any other builtinType
$dimensions = '[]';
while (null !== $innerType->getCollectionValueType() && Type::BUILTIN_TYPE_ARRAY === $innerType->getBuiltinType()) {
$dimensions .= '[]';
$innerType = $innerType->getCollectionValueType();
}
if (null !== $innerType->getClassName()) {
// the builtinType is the inner one and the class is the class followed by []...[]
$builtinType = $innerType->getBuiltinType();
$class = $innerType->getClassName().$dimensions;
} else {
// default fallback (keep it as array)
$builtinType = $type->getBuiltinType();
$class = $type->getClassName();
}
} else {
$builtinType = $type->getBuiltinType();
$class = $type->getClassName();

View File

@ -27,6 +27,7 @@ use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Serializer\Tests\Fixtures\Dummy;
use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy;
use Symfony\Component\Serializer\Tests\Fixtures\GroupDummyChild;
use Symfony\Component\Serializer\Tests\Fixtures\PropertyCircularReferenceDummy;
@ -407,6 +408,52 @@ class PropertyNormalizerTest extends TestCase
{
$this->assertTrue($this->normalizer->supportsNormalization(new PropertyChildDummy()));
}
public function testMultiDimensionObject()
{
$normalizer = $this->getDenormalizerForTypeEnforcement();
$root = $normalizer->denormalize([
'children' => [[
['foo' => 'one', 'bar' => 'two'],
['foo' => 'three', 'bar' => 'four'],
]],
'grandChildren' => [[[
['foo' => 'five', 'bar' => 'six'],
['foo' => 'seven', 'bar' => 'eight'],
]]],
'intMatrix' => [
[0, 1, 2],
[3, 4, 5],
],
],
RootDummy::class,
'any'
);
$this->assertEquals(\get_class($root), RootDummy::class);
// children (two dimension array)
$this->assertCount(1, $root->children);
$this->assertCount(2, $root->children[0]);
$firstChild = $root->children[0][0];
$this->assertInstanceOf(Dummy::class, $firstChild);
$this->assertSame('one', $firstChild->foo);
$this->assertSame('two', $firstChild->bar);
// grand children (three dimension array)
$this->assertCount(1, $root->grandChildren);
$this->assertCount(1, $root->grandChildren[0]);
$this->assertCount(2, $root->grandChildren[0][0]);
$firstGrandChild = $root->grandChildren[0][0][0];
$this->assertInstanceOf(Dummy::class, $firstGrandChild);
$this->assertSame('five', $firstGrandChild->foo);
$this->assertSame('six', $firstGrandChild->bar);
// int matrix
$this->assertSame([
[0, 1, 2],
[3, 4, 5],
], $root->intMatrix);
}
}
class PropertyDummy
@ -472,3 +519,34 @@ class PropertyParentDummy
class PropertyChildDummy extends PropertyParentDummy
{
}
class RootDummy
{
public $children;
public $grandChildren;
public $intMatrix;
/**
* @return Dummy[][]
*/
public function getChildren(): array
{
return $this->children;
}
/**
* @return Dummy[][][]
*/
public function getGrandChildren()
{
return $this->grandChildren;
}
/**
* @return array
*/
public function getIntMatrix()
{
return $this->intMatrix;
}
}