Skip to content

Commit f03696e

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 f03696e

File tree

5 files changed

+77
-24
lines changed

5 files changed

+77
-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/AbstractObjectNormalizerTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,16 @@
4242
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
4343
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
4444
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
45+
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
4546
use Symfony\Component\Serializer\Serializer;
4647
use Symfony\Component\Serializer\SerializerAwareInterface;
4748
use Symfony\Component\Serializer\SerializerInterface;
4849
use Symfony\Component\Serializer\Tests\Fixtures\Attributes\AbstractDummy;
4950
use Symfony\Component\Serializer\Tests\Fixtures\Attributes\AbstractDummyFirstChild;
5051
use Symfony\Component\Serializer\Tests\Fixtures\Attributes\AbstractDummySecondChild;
5152
use Symfony\Component\Serializer\Tests\Fixtures\DummyFirstChildQuux;
53+
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface;
54+
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberFour;
5255
use Symfony\Component\Serializer\Tests\Fixtures\DummySecondChildQuux;
5356
use Symfony\Component\Serializer\Tests\Fixtures\DummyString;
5457
use Symfony\Component\Serializer\Tests\Fixtures\DummyWithNotNormalizable;
@@ -1087,6 +1090,25 @@ public static function provideBooleanTypesData()
10871090
[['foo' => false], TruePropertyDummy::class],
10881091
];
10891092
}
1093+
1094+
public function testDeserializeAndSerializeConstructorAndIgnoreAndInterfacedObjectsWithTheClassMetadataDiscriminator()
1095+
{
1096+
$example = new DummyMessageNumberFour('Hello');
1097+
1098+
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
1099+
1100+
$normalizer = new PropertyNormalizer(
1101+
$classMetadataFactory,
1102+
null,
1103+
new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]),
1104+
new ClassDiscriminatorFromClassMetadata($classMetadataFactory),
1105+
);
1106+
1107+
$serialized = $normalizer->normalize($example, 'json');
1108+
$deserialized = $normalizer->denormalize($serialized, DummyMessageInterface::class, 'json');
1109+
1110+
$this->assertEquals($example, $deserialized);
1111+
}
10901112
}
10911113

10921114
class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer

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