Skip to content

Commit a7d76a1

Browse files
committed
bug #61887 [Serializer] Fix discriminator class mapping with allow_extra_attributes=false
Make sure that the discriminator mapping type property is considered an allowed attribute when the `AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES` context option is set to `false`.
1 parent 48d0477 commit a7d76a1

File tree

6 files changed

+84
-0
lines changed

6 files changed

+84
-0
lines changed

Normalizer/GetSetMethodNormalizer.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ protected function isAllowedAttribute($classOrObject, string $attribute, ?string
190190

191191
$class = \is_object($classOrObject) ? $classOrObject::class : $classOrObject;
192192

193+
if ($this->classDiscriminatorResolver?->getMappingForMappedObject($classOrObject)?->getTypeProperty() === $attribute) {
194+
return true;
195+
}
196+
193197
if (!isset(self::$reflectionCache[$class])) {
194198
self::$reflectionCache[$class] = new \ReflectionClass($class);
195199
}

Normalizer/ObjectNormalizer.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ protected function isAllowedAttribute($classOrObject, string $attribute, ?string
168168

169169
$class = \is_object($classOrObject) ? $classOrObject::class : $classOrObject;
170170

171+
if ($this->classDiscriminatorResolver?->getMappingForMappedObject($classOrObject)?->getTypeProperty() === $attribute) {
172+
return true;
173+
}
174+
171175
if ($context['_read_attributes'] ?? true) {
172176
if (!isset(self::$isReadableCache[$class.$attribute])) {
173177
self::$isReadableCache[$class.$attribute] = $this->propertyInfoExtractor->isReadable($class, $attribute) || $this->hasAttributeAccessorMethod($class, $attribute) || (\is_object($classOrObject) && $this->propertyAccessor->isReadable($classOrObject, $attribute));

Normalizer/PropertyNormalizer.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ protected function isAllowedAttribute(object|string $classOrObject, string $attr
117117
return false;
118118
}
119119

120+
if ($this->classDiscriminatorResolver?->getMappingForMappedObject($classOrObject)?->getTypeProperty() === $attribute) {
121+
return true;
122+
}
123+
120124
try {
121125
$reflectionProperty = $this->getReflectionProperty($classOrObject, $attribute);
122126
} catch (\ReflectionException) {

Tests/Normalizer/GetSetMethodNormalizerTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,23 @@ public function testSupportsAndDenormalizeWithOptionalSetterArgument(array $data
563563
$obj = $this->normalizer->denormalize($data, GetSetDummyWithOptionalAndMultipleSetterArgs::class);
564564
$this->assertSame($expected, $obj->$method());
565565
}
566+
567+
public function testDiscriminatorWithAllowExtraAttributesFalse()
568+
{
569+
// Discriminator type property should be allowed with allow_extra_attributes=false
570+
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
571+
$discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
572+
$normalizer = new GetSetMethodNormalizer($classMetadataFactory, null, null, $discriminator);
573+
574+
$obj = $normalizer->denormalize(
575+
['type' => 'one'],
576+
GetSetMethodDummyInterface::class,
577+
null,
578+
[AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false]
579+
);
580+
581+
$this->assertInstanceOf(GetSetMethodDiscriminatedDummyOne::class, $obj);
582+
}
566583
}
567584

568585
class GetSetDummy

Tests/Normalizer/ObjectNormalizerTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Symfony\Component\Serializer\Exception\LogicException;
2424
use Symfony\Component\Serializer\Exception\RuntimeException;
2525
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
26+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
2627
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
2728
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
2829
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
@@ -1061,6 +1062,23 @@ public function testPrecedenceOfAccessorMethods()
10611062
'foo' => 'hasFoo',
10621063
], $normalizedSwappedHasserIsser);
10631064
}
1065+
1066+
public function testDiscriminatorWithAllowExtraAttributesFalse()
1067+
{
1068+
// Discriminator type property should be allowed with allow_extra_attributes=false
1069+
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
1070+
$discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
1071+
$normalizer = new ObjectNormalizer($classMetadataFactory, null, null, null, $discriminator);
1072+
1073+
$obj = $normalizer->denormalize(
1074+
['type' => 'type_a'],
1075+
DiscriminatorDummyInterface::class,
1076+
null,
1077+
[AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false]
1078+
);
1079+
1080+
$this->assertInstanceOf(DiscriminatorDummyTypeA::class, $obj);
1081+
}
10641082
}
10651083

10661084
class ProxyObjectDummy extends ObjectDummy
@@ -1404,6 +1422,25 @@ public function isolate()
14041422
}
14051423
}
14061424

1425+
#[\Symfony\Component\Serializer\Attribute\DiscriminatorMap(
1426+
typeProperty: 'type',
1427+
mapping: [
1428+
'type_a' => DiscriminatorDummyTypeA::class,
1429+
'type_b' => DiscriminatorDummyTypeB::class,
1430+
]
1431+
)]
1432+
interface DiscriminatorDummyInterface
1433+
{
1434+
}
1435+
1436+
class DiscriminatorDummyTypeA implements DiscriminatorDummyInterface
1437+
{
1438+
}
1439+
1440+
class DiscriminatorDummyTypeB implements DiscriminatorDummyInterface
1441+
{
1442+
}
1443+
14071444
class ObjectWithPropertyAndAllAccessorMethods
14081445
{
14091446
public function __construct(

Tests/Normalizer/PropertyNormalizerTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
2323
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
2424
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;
25+
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
2526
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
2627
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
2728
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
@@ -551,6 +552,23 @@ public function testDenormalizeWithDiscriminator()
551552

552553
$this->assertEquals($denormalized, $normalizer->denormalize(['type' => 'two', 'url' => 'url'], PropertyDummyInterface::class));
553554
}
555+
556+
public function testDiscriminatorWithAllowExtraAttributesFalse()
557+
{
558+
// Discriminator type property should be allowed with allow_extra_attributes=false
559+
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
560+
$discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
561+
$normalizer = new PropertyNormalizer($classMetadataFactory, null, null, $discriminator);
562+
563+
$obj = $normalizer->denormalize(
564+
['type' => 'one'],
565+
PropertyDummyInterface::class,
566+
null,
567+
[AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false]
568+
);
569+
570+
$this->assertInstanceOf(PropertyDiscriminatedDummyOne::class, $obj);
571+
}
554572
}
555573

556574
class PropertyDummy

0 commit comments

Comments
 (0)