Symfony version(s) affected
^6.0
Description
A TypeError is thrown by the Symfony serializer when trying to denormalize a string value into object.
class Author {
public $name;
}
class Blog {
public $title;
public Author $author;
}
// when there is no issue
$blog = $denormalizer->denormalize([
'title' => 'test title',
'author' => [
'name' => 'test name',
],
], Blog::class);
// when a type error is thrown
$blog = $denormalizer->denormalize([
'title' => 'test title',
'author' => 'test string',
], Blog::class);
The expected behavior is the deserializer throwing a Symfony\Component\Serializer\Exception\UnexpectedValueException exception.
When recieving JSON through an API this can be quite annoying if you want to tell the user that they sent malformed json. You'd currently have to catch the ValueException which is not ideal.
How to reproduce
The issue can be reproducted by cloning this repository:
TypeError:
Symfony\Component\Serializer\Normalizer\AbstractNormalizer::prepareForDenormalization(): Argument #1 ($data) must be of type object|array|null, string given, called in /var/www/symfony/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php on line 368
at vendor/symfony/serializer/Normalizer/AbstractNormalizer.php:299
at Symfony\Component\Serializer\Normalizer\AbstractNormalizer->prepareForDenormalization('test string')
(vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:368)
at Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer->denormalize('test string', 'App\\Entity\\User', null, array('cache_key' => 'c93a6d4efa206ea58a62cc6b7fab8dfb', 'deserialization_path' => 'author'))
(vendor/symfony/serializer/Serializer.php:238)
at Symfony\Component\Serializer\Serializer->denormalize('test string', 'App\\Entity\\User', null, array('cache_key' => 'c93a6d4efa206ea58a62cc6b7fab8dfb', 'deserialization_path' => 'author'))
(vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:559)
at Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer->validateAndDenormalize(array(object(Type)), 'App\\Entity\\Blog', 'author', 'test string', null, array('cache_key' => '44db5a926a1544b1a8585af40107ca3a', 'deserialization_path' => 'author'))
(vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:401)
at Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer->denormalize(array('title' => 'test title', 'author' => 'test string'), 'App\\Entity\\Blog', null, array('cache_key' => '44db5a926a1544b1a8585af40107ca3a'))
(vendor/symfony/serializer/Serializer.php:238)
at Symfony\Component\Serializer\Serializer->denormalize(array('title' => 'test title', 'author' => 'test string'), 'App\\Entity\\Blog')
(src/Controller/BugReproductionController.php:19)
at App\Controller\BugReproductionController->test(object(Serializer))
(vendor/symfony/http-kernel/HttpKernel.php:152)
at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
(vendor/symfony/http-kernel/HttpKernel.php:74)
at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
(vendor/symfony/http-kernel/Kernel.php:202)
at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
(vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35)
at Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
(vendor/autoload_runtime.php:29)
at require_once('/var/www/symfony/vendor/autoload_runtime.php')
(public/index.php:5)
Possible Solution
I think a passable solution would be to add type validation in the Symfony\Component\Serializer\Normalizer\AbstractNormalizer::prepareForDenormalization function
To give some extra context, the function is called here:
|
public function denormalize(mixed $data, string $type, string $format = null, array $context = []) |
|
{ |
|
if (!isset($context['cache_key'])) { |
|
$context['cache_key'] = $this->getCacheKey($format, $context); |
|
} |
|
|
|
$allowedAttributes = $this->getAllowedAttributes($type, $context, true); |
|
$normalizedData = $this->prepareForDenormalization($data); |
|
$extraAttributes = []; |
|
|
|
$reflectionClass = new \ReflectionClass($type); |
|
$object = $this->instantiateObject($normalizedData, $type, $context, $reflectionClass, $allowedAttributes, $format); |
|
$resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object); |
|
|
|
foreach ($normalizedData as $attribute => $value) { |
|
$attributeContext = $this->getAttributeDenormalizationContext($resolvedClass, $attribute, $context); |
Without the fix:
/**
* Normalizes the given data to an array. It's particularly useful during
* the denormalization process.
*/
protected function prepareForDenormalization(object|array|null $data): array
{
return (array) $data;
}
With the fix
/**
* Normalizes the given data to an array. It's particularly useful during
* the denormalization process.
*/
protected function prepareForDenormalization(mixed $data): array
{
if (is_scalar($data)) {
throw new Symfony\Component\Serializer\Exception\UnexpectedValueException();
}
return (array) $data;
}
Additional Context
No response
Symfony version(s) affected
^6.0
Description
A TypeError is thrown by the Symfony serializer when trying to denormalize a string value into object.
The expected behavior is the deserializer throwing a
Symfony\Component\Serializer\Exception\UnexpectedValueExceptionexception.When recieving JSON through an API this can be quite annoying if you want to tell the user that they sent malformed json. You'd currently have to catch the ValueException which is not ideal.
How to reproduce
The issue can be reproducted by cloning this repository:
git clone git@github.com:JustDylan23/blog.git)git checkout bug-showcaseA type error should be thrown at this point.
Possible Solution
I think a passable solution would be to add type validation in the
Symfony\Component\Serializer\Normalizer\AbstractNormalizer::prepareForDenormalizationfunctionTo give some extra context, the function is called here:
symfony/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
Lines 361 to 376 in cef3d5a
Without the fix:
With the fix
Additional Context
No response