diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 69dc534e417fb..98ccfca4e4702 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -780,6 +780,10 @@ private function removeNestedValue(array $path, array $data): array */ private function getMappedClass(array $data, string $class, array $context): string { + if (null !== $object = $this->extractObjectToPopulate($class, $context, self::OBJECT_TO_POPULATE)) { + return $object::class; + } + if (!$mapping = $this->classDiscriminatorResolver?->getMappingForClass($class)) { return $class; } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index d876193c8a741..53ef55bd1bf70 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -34,6 +34,7 @@ use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter; +use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; @@ -44,6 +45,7 @@ use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummy; use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummyFirstChild; use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummySecondChild; +use Symfony\Component\Serializer\Tests\Fixtures\DummyFirstChildQuux; use Symfony\Component\Serializer\Tests\Fixtures\DummySecondChildQuux; use Symfony\Component\Serializer\Tests\Normalizer\Features\ObjectDummyWithContextAttribute; @@ -480,6 +482,61 @@ public function hasMetadataFor($value): bool $this->assertInstanceOf(DummySecondChildQuux::class, $normalizedData->quux); } + public function testDenormalizeWithDiscriminatorMapAndObjectToPopulateUsesCorrectClassname() + { + $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + + $loaderMock = new class() implements ClassMetadataFactoryInterface { + public function getMetadataFor($value): ClassMetadataInterface + { + if (AbstractDummy::class === $value) { + return new ClassMetadata( + AbstractDummy::class, + new ClassDiscriminatorMapping('type', [ + 'first' => AbstractDummyFirstChild::class, + 'second' => AbstractDummySecondChild::class, + ]) + ); + } + + throw new InvalidArgumentException(); + } + + public function hasMetadataFor($value): bool + { + return AbstractDummy::class === $value; + } + }; + + $discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock); + $normalizer = new AbstractObjectNormalizerDummy($factory, null, new PhpDocExtractor(), $discriminatorResolver); + $serializer = new Serializer([$normalizer]); + $normalizer->setSerializer($serializer); + + $data = [ + 'foo' => 'foo', + 'quux' => ['value' => 'quux'], + ]; + + $normalizedData1 = $normalizer->denormalize($data + ['bar' => 'bar'], AbstractDummy::class, 'any', [ + AbstractNormalizer::OBJECT_TO_POPULATE => new AbstractDummyFirstChild('notfoo', 'notbar'), + ]); + $this->assertInstanceOf(AbstractDummyFirstChild::class, $normalizedData1); + $this->assertSame('foo', $normalizedData1->foo); + $this->assertSame('notbar', $normalizedData1->bar); + $this->assertInstanceOf(DummyFirstChildQuux::class, $normalizedData1->quux); + $this->assertSame('quux', $normalizedData1->quux->getValue()); + + $normalizedData2 = $normalizer->denormalize($data + ['baz' => 'baz'], AbstractDummy::class, 'any', [ + AbstractNormalizer::OBJECT_TO_POPULATE => new AbstractDummySecondChild('notfoo', 'notbaz'), + ]); + $this->assertInstanceOf(AbstractDummySecondChild::class, $normalizedData2); + $this->assertSame('foo', $normalizedData2->foo); + $this->assertSame('baz', $normalizedData2->baz); + $this->assertInstanceOf(DummySecondChildQuux::class, $normalizedData2->quux); + $this->assertSame('quux', $normalizedData2->quux->getValue()); + } + public function testDenormalizeWithNestedDiscriminatorMap() { $classDiscriminatorResolver = new class() implements ClassDiscriminatorResolverInterface {