From aa2c3c87e06d9a10a61ae9bbb9d29ec1bdc966db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20de=20Guillebon?= Date: Tue, 22 Apr 2025 15:39:30 +0200 Subject: [PATCH] feat: backport handling of union/intersection type in item normalizer --- src/Serializer/AbstractItemNormalizer.php | 57 +++++++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php index 5a8a2989b47..7c12bf211d6 100644 --- a/src/Serializer/AbstractItemNormalizer.php +++ b/src/Serializer/AbstractItemNormalizer.php @@ -883,6 +883,7 @@ private function createAndValidateAttributeValue(string $attribute, mixed $value $propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $attribute, $this->getFactoryOptions($context)); $types = $propertyMetadata->getBuiltinTypes() ?? []; $isMultipleTypes = \count($types) > 1; + $denormalizationException = null; foreach ($types as $type) { if (null === $value && ($type->isNullable() || ($context[static::DISABLE_TYPE_ENFORCEMENT] ?? false))) { @@ -908,7 +909,18 @@ private function createAndValidateAttributeValue(string $attribute, mixed $value $context['resource_class'] = $resourceClass; unset($context['uri_variables']); - return $this->denormalizeCollection($attribute, $propertyMetadata, $type, $resourceClass, $value, $format, $context); + try { + return $this->denormalizeCollection($attribute, $propertyMetadata, $type, $resourceClass, $value, $format, $context); + } catch (NotNormalizableValueException $e) { + // union/intersect types: try the next type, if not valid, an exception will be thrown at the end + if ($isMultipleTypes) { + $denormalizationException ??= $e; + + continue; + } + + throw $e; + } } if ( @@ -918,7 +930,18 @@ private function createAndValidateAttributeValue(string $attribute, mixed $value $resourceClass = $this->resourceClassResolver->getResourceClass(null, $className); $childContext = $this->createChildContext($this->createOperationContext($context, $resourceClass), $attribute, $format); - return $this->denormalizeRelation($attribute, $propertyMetadata, $resourceClass, $value, $format, $childContext); + try { + return $this->denormalizeRelation($attribute, $propertyMetadata, $resourceClass, $value, $format, $childContext); + } catch (NotNormalizableValueException $e) { + // union/intersect types: try the next type, if not valid, an exception will be thrown at the end + if ($isMultipleTypes) { + $denormalizationException ??= $e; + + continue; + } + + throw $e; + } } if ( @@ -933,7 +956,18 @@ private function createAndValidateAttributeValue(string $attribute, mixed $value unset($context['resource_class'], $context['uri_variables']); - return $this->serializer->denormalize($value, $className.'[]', $format, $context); + try { + return $this->serializer->denormalize($value, $className.'[]', $format, $context); + } catch (NotNormalizableValueException $e) { + // union/intersect types: try the next type, if not valid, an exception will be thrown at the end + if ($isMultipleTypes) { + $denormalizationException ??= $e; + + continue; + } + + throw $e; + } } if (null !== $className = $type->getClassName()) { @@ -943,7 +977,18 @@ private function createAndValidateAttributeValue(string $attribute, mixed $value unset($context['resource_class'], $context['uri_variables']); - return $this->serializer->denormalize($value, $className, $format, $context); + try { + return $this->serializer->denormalize($value, $className, $format, $context); + } catch (NotNormalizableValueException $e) { + // union/intersect types: try the next type, if not valid, an exception will be thrown at the end + if ($isMultipleTypes) { + $denormalizationException ??= $e; + + continue; + } + + throw $e; + } } /* From @see AbstractObjectNormalizer::validateAndDenormalize() */ @@ -1019,6 +1064,10 @@ private function createAndValidateAttributeValue(string $attribute, mixed $value } } + if ($denormalizationException) { + throw $denormalizationException; + } + return $value; }