From bd020a578d786952cf5d67f8140dfacc161f58a4 Mon Sep 17 00:00:00 2001 From: Thibault Buathier Date: Fri, 24 Jun 2022 12:01:15 +0200 Subject: [PATCH] [Serializer] Fix denormalization union types with constructor --- Normalizer/AbstractObjectNormalizer.php | 16 +++++++++++++ Tests/SerializerTest.php | 32 ++++++++++++++++++------- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/Normalizer/AbstractObjectNormalizer.php b/Normalizer/AbstractObjectNormalizer.php index 36198fcb5..fbe5d25d4 100644 --- a/Normalizer/AbstractObjectNormalizer.php +++ b/Normalizer/AbstractObjectNormalizer.php @@ -20,6 +20,7 @@ use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Exception\ExtraAttributesException; use Symfony\Component\Serializer\Exception\LogicException; +use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\Exception\RuntimeException; use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; @@ -395,6 +396,8 @@ abstract protected function setAttributeValue($object, $attribute, $value, $form * @return mixed * * @throws NotNormalizableValueException + * @throws ExtraAttributesException + * @throws MissingConstructorArgumentsException * @throws LogicException */ private function validateAndDenormalize(string $currentClass, string $attribute, $data, ?string $format, array $context) @@ -406,6 +409,7 @@ private function validateAndDenormalize(string $currentClass, string $attribute, $expectedTypes = []; $isUnionType = \count($types) > 1; $extraAttributesException = null; + $missingConstructorArgumentException = null; foreach ($types as $type) { if (null === $data && $type->isNullable()) { return null; @@ -503,6 +507,14 @@ private function validateAndDenormalize(string $currentClass, string $attribute, if (!$extraAttributesException) { $extraAttributesException = $e; } + } catch (MissingConstructorArgumentsException $e) { + if (!$isUnionType) { + throw $e; + } + + if (!$missingConstructorArgumentException) { + $missingConstructorArgumentException = $e; + } } } @@ -510,6 +522,10 @@ private function validateAndDenormalize(string $currentClass, string $attribute, throw $extraAttributesException; } + if ($missingConstructorArgumentException) { + throw $missingConstructorArgumentException; + } + if ($context[self::DISABLE_TYPE_ENFORCEMENT] ?? $this->defaultContext[self::DISABLE_TYPE_ENFORCEMENT] ?? false) { return $data; } diff --git a/Tests/SerializerTest.php b/Tests/SerializerTest.php index b5a0f1939..df8732d29 100644 --- a/Tests/SerializerTest.php +++ b/Tests/SerializerTest.php @@ -586,20 +586,26 @@ public function testUnionTypeDeserializableWithoutAllowedExtraAttributes() ['json' => new JsonEncoder()] ); - $actual = $serializer->deserialize('{ "v": { "a": 0 }}', DummyUnionWithAAndB::class, 'json', [ + $actual = $serializer->deserialize('{ "v": { "a": 0 }}', DummyUnionWithAAndCAndB::class, 'json', [ AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false, ]); - $this->assertEquals(new DummyUnionWithAAndB(new DummyATypeForUnion()), $actual); + $this->assertEquals(new DummyUnionWithAAndCAndB(new DummyATypeForUnion()), $actual); - $actual = $serializer->deserialize('{ "v": { "b": 1 }}', DummyUnionWithAAndB::class, 'json', [ + $actual = $serializer->deserialize('{ "v": { "b": 1 }}', DummyUnionWithAAndCAndB::class, 'json', [ AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false, ]); - $this->assertEquals(new DummyUnionWithAAndB(new DummyBTypeForUnion()), $actual); + $this->assertEquals(new DummyUnionWithAAndCAndB(new DummyBTypeForUnion()), $actual); + + $actual = $serializer->deserialize('{ "v": { "c": 3 }}', DummyUnionWithAAndCAndB::class, 'json', [ + AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false, + ]); + + $this->assertEquals(new DummyUnionWithAAndCAndB(new DummyCTypeForUnion(3)), $actual); $this->expectException(ExtraAttributesException::class); - $serializer->deserialize('{ "v": { "b": 1, "c": "i am not allowed" }}', DummyUnionWithAAndB::class, 'json', [ + $serializer->deserialize('{ "v": { "b": 1, "d": "i am not allowed" }}', DummyUnionWithAAndCAndB::class, 'json', [ AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false, ]); } @@ -719,13 +725,23 @@ class DummyBTypeForUnion public $b = 1; } -class DummyUnionWithAAndB +class DummyCTypeForUnion +{ + public $c = 2; + + public function __construct($c) + { + $this->c = $c; + } +} + +class DummyUnionWithAAndCAndB { - /** @var DummyATypeForUnion|DummyBTypeForUnion */ + /** @var DummyATypeForUnion|DummyCTypeForUnion|DummyBTypeForUnion */ public $v; /** - * @param DummyATypeForUnion|DummyBTypeForUnion $v + * @param DummyATypeForUnion|DummyCTypeForUnion|DummyBTypeForUnion $v */ public function __construct($v) {