From 978d1a041a77f8593d410fda6f5f939dcf2afadf Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Wed, 9 Mar 2022 15:33:52 +0100 Subject: [PATCH] [Serializer] Fix nested deserialization_path computation when there is no metadata for the attribute --- .../Normalizer/AbstractObjectNormalizer.php | 4 +- .../Serializer/Tests/Fixtures/Php74Full.php | 7 ++ .../Serializer/Tests/SerializerTest.php | 73 ++++++++++++++----- 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 885e554b4593a..73446002a9ea2 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -239,12 +239,12 @@ private function getAttributeNormalizationContext(object $object, string $attrib */ private function getAttributeDenormalizationContext(string $class, string $attribute, array $context): array { + $context['deserialization_path'] = ($context['deserialization_path'] ?? false) ? $context['deserialization_path'].'.'.$attribute : $attribute; + if (null === $metadata = $this->getAttributeMetadata($class, $attribute)) { return $context; } - $context['deserialization_path'] = ($context['deserialization_path'] ?? false) ? $context['deserialization_path'].'.'.$attribute : $attribute; - return array_merge($context, $metadata->getDenormalizationContextForGroups($this->getGroups($context))); } diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/Php74Full.php b/src/Symfony/Component/Serializer/Tests/Fixtures/Php74Full.php index 4f3186c30e94b..8b53906c405dc 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/Php74Full.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/Php74Full.php @@ -29,6 +29,8 @@ final class Php74Full public array $collection; public Php74FullWithConstructor $php74FullWithConstructor; public DummyMessageInterface $dummyMessage; + /** @var TestFoo[] $nestedArray */ + public TestFoo $nestedObject; } @@ -38,3 +40,8 @@ public function __construct($constructorArgument) { } } + +final class TestFoo +{ + public int $int; +} diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 5fc511dc8a715..4fb07fde2048e 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -739,8 +739,12 @@ public function testDeserializeAndUnwrap() ); } - /** @requires PHP 7.4 */ - public function testCollectDenormalizationErrors() + /** + * @dataProvider provideCollectDenormalizationErrors + * + * @requires PHP 7.4 + */ + public function testCollectDenormalizationErrors(?ClassMetadataFactory $classMetadataFactory) { $json = ' { @@ -764,10 +768,12 @@ public function testCollectDenormalizationErrors() ], "php74FullWithConstructor": {}, "dummyMessage": { + }, + "nestedObject": { + "int": "string" } }'; - $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); $extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]); $serializer = new Serializer( @@ -777,7 +783,7 @@ public function testCollectDenormalizationErrors() new DateTimeZoneNormalizer(), new DataUriNormalizer(), new UidNormalizer(), - new ObjectNormalizer($classMetadataFactory, null, null, $extractor, new ClassDiscriminatorFromClassMetadata($classMetadataFactory)), + new ObjectNormalizer($classMetadataFactory, null, null, $extractor, $classMetadataFactory ? new ClassDiscriminatorFromClassMetadata($classMetadataFactory) : null), ], ['json' => new JsonEncoder()] ); @@ -913,22 +919,45 @@ public function testCollectDenormalizationErrors() 'useMessageForUser' => true, 'message' => 'Failed to create object because the object miss the "constructorArgument" property.', ], + $classMetadataFactory ? + [ + 'currentType' => 'null', + 'expectedTypes' => [ + 'string', + ], + 'path' => 'dummyMessage.type', + 'useMessageForUser' => false, + 'message' => 'Type property "type" not found for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface".', + ] : + [ + 'currentType' => 'array', + 'expectedTypes' => [ + DummyMessageInterface::class, + ], + 'path' => 'dummyMessage', + 'useMessageForUser' => false, + 'message' => 'The type of the "dummyMessage" attribute for class "Symfony\Component\Serializer\Tests\Fixtures\Php74Full" must be one of "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface" ("array" given).', + ], [ - 'currentType' => 'null', + 'currentType' => 'string', 'expectedTypes' => [ - 'string', + 'int', ], - 'path' => 'dummyMessage.type', - 'useMessageForUser' => false, - 'message' => 'Type property "type" not found for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface".', + 'path' => 'nestedObject[int]', + 'useMessageForUser' => true, + 'message' => 'The type of the key "int" must be "int" ("string" given).', ], ]; $this->assertSame($expected, $exceptionsAsArray); } - /** @requires PHP 7.4 */ - public function testCollectDenormalizationErrors2() + /** + * @dataProvider provideCollectDenormalizationErrors + * + * @requires PHP 7.4 + */ + public function testCollectDenormalizationErrors2(?ClassMetadataFactory $classMetadataFactory) { $json = ' [ @@ -940,13 +969,12 @@ public function testCollectDenormalizationErrors2() } ]'; - $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); $extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]); $serializer = new Serializer( [ new ArrayDenormalizer(), - new ObjectNormalizer($classMetadataFactory, null, null, $extractor, new ClassDiscriminatorFromClassMetadata($classMetadataFactory)), + new ObjectNormalizer($classMetadataFactory, null, null, $extractor, $classMetadataFactory ? new ClassDiscriminatorFromClassMetadata($classMetadataFactory) : null), ], ['json' => new JsonEncoder()] ); @@ -999,17 +1027,20 @@ public function testCollectDenormalizationErrors2() $this->assertSame($expected, $exceptionsAsArray); } - /** @requires PHP 8.0 */ - public function testCollectDenormalizationErrorsWithConstructor() + /** + * @dataProvider provideCollectDenormalizationErrors + * + * @requires PHP 8.0 + */ + public function testCollectDenormalizationErrorsWithConstructor(?ClassMetadataFactory $classMetadataFactory) { $json = '{"bool": "bool"}'; - $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); $extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]); $serializer = new Serializer( [ - new ObjectNormalizer($classMetadataFactory, null, null, $extractor, new ClassDiscriminatorFromClassMetadata($classMetadataFactory)), + new ObjectNormalizer($classMetadataFactory, null, null, $extractor, $classMetadataFactory ? new ClassDiscriminatorFromClassMetadata($classMetadataFactory) : null), ], ['json' => new JsonEncoder()] ); @@ -1050,6 +1081,14 @@ public function testCollectDenormalizationErrorsWithConstructor() $this->assertSame($expected, $exceptionsAsArray); } + + public function provideCollectDenormalizationErrors() + { + return [ + [null], + [new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))], + ]; + } } class Model