8000 [Serializer] Add support for discriminator map in property normalizer · symfony/symfony@ad5ce53 · GitHub
[go: up one dir, main page]

Skip to content

Commit ad5ce53

Browse files
committed
[Serializer] Add support for discriminator map in property normalizer
Fixes #60214 Currently it's not possible to serialize an object using the PropertyNormalizer when a DiscriminatorMap attribute is used. It produces the following error: > Symfony\Component\Serializer\Exception\NotNormalizableValueException: Type property "type" not found > for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface". The ObjectNormalizer overrides the `getAllowedAttributes` from AbstractNormalizer and adds support for discriminators. But the PropertyNormalizer does not do this. Therefore it doesn't work. For now, we copy the logic from ObjectNormalizer to PropertyNormalizer and the problem goes away.
1 parent f8cca42 commit ad5ce53

File tree

4 files changed

+76
-0
lines changed

4 files changed

+76
-0
lines changed

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
1515
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
16+
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
1617
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
1718
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
1819
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
@@ -221,4 +222,28 @@ private function getReflectionProperty(string|object $classOrObject, string $att
221222
}
222223
}
223224
}
225+
226+
protected function getAllowedAttributes(string|object $classOrObject, array $context, bool $attributesAsString = false): array|bool
227+
{
228+
if (false === $allowedAttributes = parent::getAllowedAttributes($classOrObject, $context, $attributesAsString)) {
229+
return false;
230+
}
231+
232+
if (null !== $this->classDiscriminatorResolver) {
233+
$class = \is_object($classOrObject) ? $classOrObject::class : $classOrObject;
234+
if (null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForMappedObject($classOrObject)) {
235+
$allowedAttributes[] = $attributesAsString ? $discriminatorMapping->getTypeProperty() : new AttributeMetadata($discriminatorMapping->getTypeProperty());
236+
}
237+
238+
if (null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
239+
$attributes = [];
240+
foreach ($discriminatorMapping->getTypesMapping() as $mappedClass) {
241+
$attributes[] = parent::getAllowedAttributes($mappedClass, $context, $attributesAsString);
242+
}
243+
$allowedAttributes = array_merge($allowedAttributes, ...$attributes);
244+
}
245+
}
246+
247+
return $allowedAttributes;
248+
}
224249
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
'one' => DummyMessageNumberOne::class,
2121
'two' => DummyMessageNumberTwo::class,
2222
'three' => DummyMessageNumberThree::class,
23+
'four' => DummyMessageNumberFour::class,
2324
])]
2425
interface DummyMessageInterface
2526
{
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Tests\Fixtures;
13+
14+
use Symfony\Component\Serializer\Attribute\Ignore;
15+
16+
abstract class SomeAbstract {
17+
#[Ignore]
18+
public function getDescription()
19+
{
20+
return 'Hello, World!';
21+
}
22+
}
23+
24+
class DummyMessageNumberFour extends SomeAbstract implements DummyMessageInterface
25+
{
26+
public function __construct(public $one)
27+
{
28+
}
29+
}

src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
use Symfony\Component\Serializer\Tests\Fixtures\Attributes\GroupDummy;
3232
use Symfony\Component\Serializer\Tests\Fixtures\Attributes\GroupDummyChild;
3333
use Symfony\Component\Serializer\Tests\Fixtures\Dummy;
34+
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface;
35+
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberFour;
3436
use Symfony\Component\Serializer\Tests\Fixtures\Php74Dummy;
3537
use Symfony\Component\Serializer\Tests\Fixtures\PropertyCircularReferenceDummy;
3638
use Symfony\Component\Serializer\Tests\Fixtures\PropertySiblingHolder;
@@ -516,6 +518,25 @@ public function testDenormalizeWithDiscriminator()
516518

517519
$this->assertEquals($denormalized, $normalizer->denormalize(['type' => 'two', 'url' => 'url'], PropertyDummyInterface::class));
518520
}
521+
522+
public function testDeserializeAndSerializeConstructorAndIgnoreAndInterfacedObjectsWithTheClassMetadataDiscriminator()
523+
{
524+
$example = new DummyMessageNumberFour('Hello');
525+
526+
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
527+
528+
$normalizer = new PropertyNormalizer(
529+
$classMetadataFactory,
530+
null,
531+
new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]),
532+
new ClassDiscriminatorFromClassMetadata($classMetadataFactory),
533+
);
534+
535+
$serialized = $normalizer->normalize($example, 'json');
536+
$deserialized = $normalizer->denormalize($serialized, DummyMessageInterface::class, 'json');
537+
538+
$this->assertEquals($example, $deserialized);
539+
}
519540
}
520541

521542
class PropertyDummy

0 commit comments

Comments
 (0)
0