8000 [Serializer] Fix denormalization of arrays · symfony/symfony@4603627 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4603627

Browse files
committed
[Serializer] Fix denormalization of arrays
1 parent d7f8ca7 commit 4603627

File tree

4 files changed

+76
-13
lines changed

4 files changed

+76
-13
lines changed

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,22 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma
248248
return;
249249
}
250250

251-
$builtinType = $type->getBuiltinType();
252-
$class = $type->getClassName();
251+
if (
252+
$type->isCollection() &&
253+
null !== ($collectionValueType = $type->getCollectionValueType()) &&
254+
Type::BUILTIN_TYPE_OBJECT === ($collectionValueBuiltinType = $collectionValueType->getBuiltinType())
255+
) {
256+
$builtinType = Type::BUILTIN_TYPE_OBJECT;
257+
$class = $collectionValueType->getClassName().'[]';
258+
259+
if (null !== ($collectionKeyType = $type->getCollectionKeyType())) {
260+
$context['key_type'] = $collectionKeyType;
261+
}
262+
} else {
263+
$builtinType = $type->getBuiltinType();
264+
$class = $type->getClassName();
265+
}
266+
253267
$expectedTypes[Type::BUILTIN_TYPE_OBJECT === $builtinType && $class ? $class : $builtinType] = true;
254268

255269
if (Type::BUILTIN_TYPE_OBJECT === $builtinType) {

src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Serializer\Exception\BadMethodCallException;
1515
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
16+
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
1617
use Symfony\Component\Serializer\SerializerAwareInterface;
1718
use Symfony\Component\Serializer\SerializerInterface;
1819

@@ -30,6 +31,8 @@ class ArrayDenormalizer implements DenormalizerInterface, SerializerAwareInterfa
3031

3132
/**
3233
* {@inheritdoc}
34+
*
35+
* @throws UnexpectedValueException
3336
*/
3437
public function denormalize($data, $class, $format = null, array $context = array())
3538
{
@@ -46,12 +49,16 @@ public function denormalize($data, $class, $format = null, array $context = arra
4649
$serializer = $this->serializer;
4750
$class = substr($class, 0, -2);
4851

49-
return array_map(
50-
function ($data) use ($serializer, $class, $format, $context) {
51-
return $serializer->denormalize($data, $class, $format, $context);
52-
},
53-
$data
54-
);
52+
$builtinType = isset($context['key_type']) ? $context['key_type']->getBuiltinType() : null;
53+
foreach ($data as $key => $value) {
54+
if (null !== $builtinType && !call_user_func('is_'.$builtinType, $key)) {
55+
throw new UnexpectedValueException(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, $builtinType, gettype($key)));
56+
}
57+
58+
$data[$key] = $serializer->denormalize($value, $class, $format, $context);
59+
}
60+
61+
return $data;
5562
}
5663

5764
/**

src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
namespace Symfony\Component\Serializer\Tests\Normalizer;
1313

1414
use Doctrine\Common\Annotations\AnnotationReader;
15+
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
1516
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
17+
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
1618
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
1719
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
20+
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
1821
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
1922
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
2023
use Symfony\Component\Serializer\Serializer;
@@ -525,13 +528,21 @@ public function testThrowUnexpectedValueException()
525528

526529
public function testDenomalizeRecursive()
527530
{
528-
$normalizer = new ObjectNormalizer(null, null, null, new ReflectionExtractor());
529-
$serializer = new Serializer(array(new DateTimeNormalizer(), $normalizer));
531+
$extractor = new PropertyInfoExtractor(array(), array(new PhpDocExtractor(), new ReflectionExtractor()));
532+
$normalizer = new ObjectNormalizer(null, null, null, $extractor);
533+
$serializer = new Serializer(array(new ArrayDenormalizer(), new DateTimeNormalizer(), $normalizer));
534+
535+
$obj = $serializer->denormalize(array(
536+
'inner' => array('foo' => 'foo', 'bar' => 'bar'),
537+
'date' => '1988/01/21',
538+
'inners' => array(array('foo' => 1), array('foo' => 2)),
539+
), ObjectOuter::class);
530540

531-
$obj = $serializer->denormalize(array('inner' => array('foo' => 'foo', 'bar' => 'bar'), 'date' => '1988/01/21'), ObjectOuter::class);
532541
$this->assertEquals('foo', $obj->getInner()->foo);
533542
$this->assertEquals('bar', $obj->getInner()->bar);
534543
$this->assertEquals('1988-01-21', $obj->getDate()->format('Y-m-d'));
544+
$this->assertEquals(1, $obj->getInners()[0]->foo);
545+
$this->assertEquals(2, $obj->getInners()[1]->foo);
535546
}
536547

537548
/**
@@ -546,6 +557,21 @@ public function testRejectInvalidType()
546557
$serializer->denormalize(array('date' => 'foo'), ObjectOuter::class);
547558
}
548559

560+
/**
561+
* @expectedException UnexpectedValueException
562+
* @expectedExceptionMessage The type of the key "a" must be "int" ("string" given).
563+
*/
564+
public function testRejectInvalidKey()
565+
{
566+
$extractor = new PropertyInfoExtractor(array(), array(new PhpDocExtractor(), new ReflectionExtractor()));
567+
$normalizer = new ObjectNormalizer(null, null, null, $extractor);
568+
$serializer = new Serializer(array(new ArrayDenormalizer(), new DateTimeNormalizer(), $normalizer));
569+
570+
var_dump($serializer->denormalize(array(
571+
'inners' => array('a' => array('foo' => 1)),
572+
), ObjectOuter::class));
573+
}
574+
549575
public function testExtractAttributesRespectsFormat()
550576
{
551577
$normalizer = new FormatAndContextAwareNormalizer();
@@ -740,6 +766,11 @@ class ObjectOuter
740766
private $inner;
741767
private $date;
742768

769+
/**
770+
* @var ObjectInner[]
771+
*/
772+
private $inners;
773+
743774
public function getInner()
744775
{
745776
return $this->inner;
@@ -759,6 +790,16 @@ public function getDate()
759790
{
760791
return $this->date;
761792
}
793+
794+
public function setInners(array $inners)
795+
{
796+
$this->inners = $inners;
797+
}
798+
799+
public function getInners()
800+
{
801+
return $this->inners;
802+
}
762803
}
763804

764805
class ObjectInner

src/Symfony/Component/Serializer/composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@
2424
"symfony/property-access": "~2.8|~3.0",
2525
"symfony/http-foundation": "~2.8|~3.0",
2626
"symfony/cache": "~3.1",
27-
"symfony/property-info": "~2.8|~3.0",
27+
"symfony/property-info": "~3.1",
2828
"doctrine/annotations": "~1.0",
29-
"doctrine/cache": "~1.0"
29+
"doctrine/cache": "~1.0",
30+
"phpdocumentor/reflection-docblock": "~3.0"
3031
},
3132
"conflict": {
3233
"symfony/property-access": ">=3.0,<3.0.4|>=2.8,<2.8.4"

0 commit comments

Comments
 (0)
0