Skip to content

Commit 703536c

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 92f7862 commit 703536c

File tree

5 files changed

+76
-24
lines changed

5 files changed

+76
-24
lines changed

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use Symfony\Component\Serializer\Exception\LogicException;
2626
use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;
2727
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
28+
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
2829
use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
2930
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
3031
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
@@ -765,6 +766,30 @@ protected function createChildContext(array $parentContext, string $attribute, ?
765766
return $context;
766767
}
767768

769+
protected function getAllowedAttributes(string|object $classOrObject, array $context, bool $attributesAsString = false): array|bool
770+
{
771+
if (false === $allowedAttributes = parent::getAllowedAttributes($classOrObject, $context, $attributesAsString)) {
772+
return false;
773+
}
774+
775+
if (null !== $this->classDiscriminatorResolver) {
776+
$class = \is_object($classOrObject) ? $classOrObject::class : $classOrObject;
777+
if (null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForMappedObject($classOrObject)) {
778+
$allowedAttributes[] = $attributesAsString ? $discriminatorMapping->getTypeProperty() : new AttributeMetadata($discriminatorMapping->getTypeProperty());
779+
}
780+
781+
if (null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
782+
$attributes = [];
783+
foreach ($discriminatorMapping->getTypesMapping() as $mappedClass) {
784+
$attributes[] = parent::getAllowedAttributes($mappedClass, $context, $attributesAsString);
785+
}
786+
$allowedAttributes = array_merge($allowedAttributes, ...$attributes);
787+
}
788+
}
789+
790+
return $allowedAttributes;
791+
}
792+
768793
/**
769794
* Builds the cache key for the attributes cache.
770795
*

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

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -164,30 +164,6 @@ protected function setAttributeValue(object $object, string $attribute, mixed $v
164164
}
165165
}
166166

167-
protected function getAllowedAttributes(string|object $classOrObject, array $context, bool $attributesAsString = false): array|bool
168-
{
169-
if (false === $allowedAttributes = parent::getAllowedAttributes($classOrObject, $context, $attributesAsString)) {
170-
return false;
171-
}
172-
173-
if (null !== $this->classDiscriminatorResolver) {
174-
$class = \is_object($classOrObject) ? $classOrObject::class : $classOrObject;
175-
if (null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForMappedObject($classOrObject)) {
176-
$allowedAttributes[] = $attributesAsString ? $discriminatorMapping->getTypeProperty() : new AttributeMetadata($discriminatorMapping->getTypeProperty());
177-
}
178-
179-
if (null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
180-
$attributes = [];
181-
foreach ($discriminatorMapping->getTypesMapping() as $mappedClass) {
182-
$attributes[] = parent::getAllowedAttributes($mappedClass, $context, $attributesAsString);
183-
}
184-
$allowedAttributes = array_merge($allowedAttributes, ...$attributes);
185-
}
186-
}
187-
188-
return $allowedAttributes;
189-
}
190-
191167
protected function isAllowedAttribute($classOrObject, string $attribute, ?string $format = null, array $context = [])
192168
{
193169
if (!parent::isAllowedAttribute($classOrObject, $attribute, $format, $context)) {

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy