Skip to content

Commit c4388b6

Browse files
committed
[Serializer] Throw NotNormalizableValueException when type is not known or not in body in discriminator map
1 parent 64cbfd2 commit c4388b6

File tree

3 files changed

+50
-10
lines changed

3 files changed

+50
-10
lines changed

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
use Symfony\Component\Serializer\Exception\ExtraAttributesException;
2323
use Symfony\Component\Serializer\Exception\LogicException;
2424
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
25-
use Symfony\Component\Serializer\Exception\RuntimeException;
2625
use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
2726
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
2827
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
@@ -276,12 +275,23 @@ protected function instantiateObject(array &$data, string $class, array &$contex
276275
{
277276
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
278277
if (!isset($data[$mapping->getTypeProperty()])) {
279-
throw new RuntimeException(sprintf('Type property "%s" not found for the abstract object "%s".', $mapping->getTypeProperty(), $class));
278+
throw NotNormalizableValueException::createForUnexpectedDataType(
279+
sprintf('Type property "%s" not found for the abstract object "%s".', $mapping->getTypeProperty(), $class),
280+
null,
281+
['string'],
282+
isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(),
283+
);
280284
}
281285

282286
$type = $data[$mapping->getTypeProperty()];
283287
if (null === ($mappedClass = $mapping->getClassForType($type))) {
284-
throw new RuntimeException(sprintf('The type "%s" has no mapped class for the abstract object "%s".', $type, $class));
288+
throw NotNormalizableValueException::createForUnexpectedDataType(
289+
sprintf('The type "%s" is not a valid value.', $type),
290+
$type,
291+
['string'],
292+
isset($context['']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(),
293+
true
294+
);
285295
}
286296

287297
if ($mappedClass !== $class) {

src/Symfony/Component/Serializer/Tests/Fixtures/Php74Full.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ final class Php74Full
2828
/** @var Php74Full[] */
2929
public array $collection;
3030
public Php74FullWithConstructor $php74FullWithConstructor;
31+
public DummyMessageInterface $dummyMessage;
3132
}
3233

3334

src/Symfony/Component/Serializer/Tests/SerializerTest.php

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -484,16 +484,34 @@ public function testDeserializeAndSerializeNestedInterfacedObjectsWithTheClassMe
484484

485485
public function testExceptionWhenTypeIsNotKnownInDiscriminator()
486486
{
487-
$this->expectException(RuntimeException::class);
488-
$this->expectExceptionMessage('The type "second" has no mapped class for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface"');
489-
$this->serializerWithClassDiscriminator()->deserialize('{"type":"second","one":1}', DummyMessageInterface::class, 'json');
487+
try {
488+
$this->serializerWithClassDiscriminator()->deserialize('{"type":"second","one":1}', DummyMessageInterface::class, 'json');
489+
490+
$this->fail();
491+
} catch (\Throwable $e) {
492+
$this->assertInstanceOf(NotNormalizableValueException::class, $e);
493+
$this->assertSame('The type "second" is not a valid value.', $e->getMessage());
494+
$this->assertSame('string', $e->getCurrentType());
495+
$this->assertSame(['string'], $e->getExpectedTypes());
496+
$this->assertSame('type', $e->getPath());
497+
$this->assertTrue($e->canUseMessageForUser());
498+
}
490499
}
491500

492501
public function testExceptionWhenTypeIsNotInTheBodyToDeserialiaze()
493502
{
494-
$this->expectException(RuntimeException::class);
495-
$this->expectExceptionMessage('Type property "type" not found for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface"');
496-
$this->serializerWithClassDiscriminator()->deserialize('{"one":1}', DummyMessageInterface::class, 'json');
503+
try {
504+
$this->serializerWithClassDiscriminator()->deserialize('{"one":1}', DummyMessageInterface::class, 'json');
505+
506+
$this->fail();
507+
} catch (\Throwable $e) {
508+
$this->assertInstanceOf(NotNormalizableValueException::class, $e);
509+
$this->assertSame('Type property "type" not found for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface".', $e->getMessage());
510+
$this->assertSame('null', $e->getCurrentType());
511+
$this->assertSame(['string'], $e->getExpectedTypes());
512+
$this->assertSame('type', $e->getPath());
513+
$this->assertFalse($e->canUseMessageForUser());
514+
}
497515
}
498516

499517
public function testNotNormalizableValueExceptionMessageForAResource()
@@ -744,7 +762,9 @@ public function testCollectDenormalizationErrors()
744762
"string": null
745763
}
746764
],
747-
"php74FullWithConstructor": {}
765+
"php74FullWithConstructor": {},
766+
"dummyMessage": {
767+
}
748768
}';
749769

750770
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
@@ -893,6 +913,15 @@ public function testCollectDenormalizationErrors()
893913
'useMessageForUser' => true,
894914
'message' => 'Failed to create object because the object miss the "constructorArgument" property.',
895915
],
916+
[
917+
'currentType' => 'null',
918+
'expectedTypes' => [
919+
'string',
920+
],
921+
'path' => 'dummyMessage.type',
922+
'useMessageForUser' => false,
923+
'message' => 'Type property "type" not found for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface".',
924+
],
896925
];
897926

898927
$this->assertSame($expected, $exceptionsAsArray);

0 commit comments

Comments
 (0)