Q | A |
---|
Branch? | 6.4 |
Bug fix? | yes |
New feature? | no |
Deprecations? | no |
Issues | Fix#59721 |
License | MIT |
When using theCOLLECT_DENORMALIZATION_ERRORS
flag during denormalization, Symfony should collectall errors and report them together in aPartialDenormalizationException
.
Here is an example with two expected errors:
finalreadonlyclass Foo{publicfunction__construct(publicstring$bar,public\DateTimeInterface$createdAt, ) {}}$foo =$this->denormalizer->denormalize( data: ['createdAt' =>''], type: Foo::class,);
Expected errors
Failed to create object because the class misses the "bar" property.
The data is either not a string, an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.
When the flag is passed via thecontext
$foo =$this->denormalizer->denormalize( data: ['createdAt' =>''], type: Foo::class, context: [ DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS =>true, ],);
Both errors are correctly collected and returned.
When the flag is set viadefault_context
inframework.yaml
:
serializer:default_context:collect_denormalization_errors:true
Only one error is returned:
The data is either not a string, an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.
#Root Cause
The issue originates in the \src\Symfony\Component\Serializer\Serializer.php
,
function normalize
:
if (isset($context[DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS]) ||isset($this->defaultContext[DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS])) { unset($context[DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS]);$context['not_normalizable_value_exceptions'] = [];$errors = &$context['not_normalizable_value_exceptions'];$denormalized =$normalizer->denormalize($data,$type,$format,$context);}
The first time this block is hit, it checks for the flag either in $context or $defaultContext. If found, it initializes the error array with:
$context['not_normalizable_value_exceptions'] = [];
However, during nested denormalization (e.g., when parsing thecreatedAt
field), Symfony re-enters this code path. If the flag was provided viadefaultContext
, it is still present on re-entry. Therefore, thenot_normalizable_value_exceptions
array is reset again, losing the previously collected errors.
#My Fix
The fix is to enhance the condition with an additional check to ensure the array of errors is not already initialized:
if ( (isset($context[DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS]) ||isset($this->defaultContext[DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS])) && !isset($context['not_normalizable_value_exceptions']))
This ensures the array is only initialized once, preserving previously collected errors in recursive calls, regardless of whether the flag was passed via context or default_context.
When using the
COLLECT_DENORMALIZATION_ERRORS
flag during denormalization, Symfony should collectall errors and report them together in aPartialDenormalizationException
.Here is an example with two expected errors:
Expected errors
Failed to create object because the class misses the "bar" property.
The data is either not a string, an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.
When the flag is passed via the
context
Both errors are correctly collected and returned.
When the flag is set via
default_context
inframework.yaml
:Only one error is returned:
The data is either not a string, an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.
#Root Cause
The issue originates in the
\src\Symfony\Component\Serializer\Serializer.php
,function normalize
:The first time this block is hit, it checks for the flag either in $context or $defaultContext. If found, it initializes the error array with:
However, during nested denormalization (e.g., when parsing the
createdAt
field), Symfony re-enters this code path. If the flag was provided viadefaultContext
, it is still present on re-entry. Therefore, thenot_normalizable_value_exceptions
array is reset again, losing the previously collected errors.#My Fix
The fix is to enhance the condition with an additional check to ensure the array of errors is not already initialized:
This ensures the array is only initialized once, preserving previously collected errors in recursive calls, regardless of whether the flag was passed via context or default_context.