Skip to content

Commit 2715c78

Browse files
alexndlmclaude
andcommitted
fix(serializer): preserve deserialization path and expected type on IRI type-confusion guard
The type-confusion guard in AbstractItemNormalizer::getResourceFromIri() threw a bare NotNormalizableValueException, dropping the deserialization path and expected type. With COLLECT_DENORMALIZATION_ERRORS enabled, DeserializeProvider::createViolationFromException() then produced an empty "This value should be of type ." violation with no property path (regression from #8333 vs 4.3.13). Build the exception through createForUnexpectedDataType() so the path and expected type are preserved, while keeping it a NotNormalizableValueException so union/intersection denormalization still falls through to the next member. Closes #8352 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent be26bbe commit 2715c78

2 files changed

Lines changed: 36 additions & 1 deletion

File tree

src/Serializer/AbstractItemNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@ private function getResourceFromIri(string $data, array $context, string $resour
773773
: is_a($item, $resourceClass);
774774

775775
if (!$matchesType) {
776-
throw new NotNormalizableValueException(\sprintf('The iri "%s" does not reference the correct resource.', $data));
776+
throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('Invalid IRI "%s".', $data), $data, [$resourceClass], $context['deserialization_path'] ?? null, true);
777777
}
778778

779779
return $item;

src/Serializer/Tests/AbstractItemNormalizerTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,41 @@ public function testUnionTypeCollectionDenormalizationAcceptsAnyMember(): void
13291329
$this->assertInstanceOf(Dummy::class, $actual);
13301330
}
13311331

1332+
public function testTypeConfusionGuardPreservesPathAndExpectedType(): void
1333+
{
1334+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
1335+
1336+
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
1337+
1338+
// The IRI resolves to a Dummy while a RelatedDummy is expected: the type-confusion guard must reject it.
1339+
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
1340+
$iriConverterProphecy->getResourceFromIri(Argument::cetera())->willReturn(new Dummy());
1341+
1342+
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
1343+
$resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
1344+
$resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
1345+
1346+
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
1347+
1348+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
1349+
$serializerProphecy->willImplement(DenormalizerInterface::class);
1350+
1351+
$normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
1352+
$normalizer->setSerializer($serializerProphecy->reveal());
1353+
1354+
try {
1355+
$normalizer->denormalize('/dummies/1', RelatedDummy::class, null, [
1356+
'not_normalizable_value_exceptions' => [],
1357+
'deserialization_path' => 'relatedDummy',
1358+
]);
1359+
$this->fail('Expected a NotNormalizableValueException to be thrown.');
1360+
} catch (NotNormalizableValueException $exception) {
1361+
$this->assertSame('Invalid IRI "/dummies/1".', $exception->getMessage());
1362+
$this->assertSame('relatedDummy', $exception->getPath());
1363+
$this->assertSame([RelatedDummy::class], $exception->getExpectedTypes());
1364+
}
1365+
}
1366+
13321367
public function testDenormalizeRelationNotFoundReturnsNull(): void
13331368
{
13341369
$data = [

0 commit comments

Comments
 (0)