-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Serializer] Discriminator is removed when #[Ignore]
attribute used on unrelated method
#60214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
#[Ignore]
attribute used on unrelated method
Seems to be related to: symfony/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php Lines 246 to 247 in d5b5581
|
I'm getting a similar issue though I don't think i'm even using the #[Ignore] tag, unless there is a vendor bundle using it? on symfony/serializer v6.4.19. my discriminator is being removed and my deserialized objects are now empty of data. Same as @ruudk it is hitting the line symfony/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php Lines 246 to 247 in d5b5581 which with the false value, it is now filtering out all data keys. Will try and find which bugfix introduced this issue as it was previously working for me on 6.4.* |
Looking at my old commits and the composer.lock, I was running the deserializer with discriminator columns successfully on v6.4.9 however, my lasted commit if I downgrade from v6.4.19 to v6.4.9 that does not fix the issue, so the issue must come from another bundle? |
I have a new situation where it breaks and this is a minimal reproducer: #[\Symfony\Component\Serializer\Attribute\DiscriminatorMap::__construct(
typeProperty: 'type',
mapping: [
'user' => UserActor::class,
'system' => SystemActor::class,
],
)]
abstract readonly class Actor
{
abstract public function equals(Actor $other) : bool;
#[\Symfony\Component\Serializer\Attribute\Ignore]
public function isSystem() : bool
{
return $this instanceof SystemActor;
}
#[\Symfony\Component\Serializer\Attribute\Ignore]
public function isUser() : bool
{
return $this instanceof UserActor;
}
}
final readonly class SystemActor extends Actor
{
public function equals(Actor $other) : bool
{
return $other instanceof self;
}
}
final readonly class UserActor extends Actor
{
public function __construct(
public int $userId,
) {}
#[Override]
public function equals(Actor $other) : bool
{
if ( ! $other instanceof self) {
return false;
}
return $this->userId === $other->userId;
}
} I have the following test: #[Test]
public function it_should_serialize_system_actor() : void
{
// Arrange
$actor = new SystemActor();
// Act
$serialized = $this->serializer->serialize($actor, 'json');
// Assert
self::assertSame('{"type":"system"}', $serialized);
// Act
$deserialized = $this->serializer->deserialize($serialized, Actor::class, 'json');
// Assert
self::assertEquals($actor, $deserialized);
}
#[Test]
public function it_should_serialize_user_actor() : void
{
// Arrange
$actor = new UserActor(1);
// Act
$serialized = $this->serializer->serialize($actor, 'json');
// Assert
self::assertSame('{"type":"user","user_id":1}', $serialized);
// Act
$deserialized = $this->serializer->deserialize($serialized, Actor::class, 'json');
// Assert
self::assertEquals($actor, $deserialized);
} The first one succeeds.
When I remove the |
I think I know more why this happens. It seems that the ObjectNormalizer does correctly support the DiscriminatorMap together with Ignore. See: symfony/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php Lines 158 to 171 in cb08480
But when you use constructor promoted properties (or just public properties) you're using the PropertyNormalizer, it does not override the symfony/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php Lines 214 to 251 in cb08480
|
I think you are onto something though my example isn't using any constructor promotion or public properties. Possibly the readonly or the protected shared property? Is causing something similar to occur? |
So far I cannot reproduce it in the Symfony project with a test so maybe it does has something to do with readonly indeed. Will try, thanks! |
I just tested and removing |
I have the solution, we need to copy symfony/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php Lines 152 to 174 in cb08480
I will create a PR. |
Fixes symfony#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.
Nice work though I'm guessing I must have a different issue as that did not help resolve my empty objects:
|
Ah ignore the above example I've got a much more minimal test where it is failing for: I've taken the existing symfony/symfony project 6.4 tag tests and added an identical test to the discriminator for json but swapping in the csv formatter: private function serializerWithClassDiscriminatorCsv()
{
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
return new Serializer([new ObjectNormalizer($classMetadataFactory, null, null, new ReflectionExtractor(), new ClassDiscriminatorFromClassMetadata($classMetadataFactory))], ['csv' => new CsvEncoder()]);
}
public function testDeserializeAndSerializeInterfacedObjectsWithTheClassMetadataDiscriminatorResolverCsv()
{
$example = new DummyMessageNumberOne();
$example->one = 1;
$csvData = 'type,one,two' . PHP_EOL .'one,1,' . PHP_EOL;
$serializer = $this->serializerWithClassDiscriminatorCsv();
$deserialized = $serializer->deserialize($csvData, DummyMessageInterface::class, 'csv');
$this->assertEquals($example, $deserialized);
$serialized = $serializer->serialize($deserialized, 'csv');
$this->assertEquals($csvData, $serialized);
} give the result:
serialization works on the $example object and gives an identical string to the $csvData. It's just going the opposite directions which is failing |
I believe this is a different, but similar issue. |
Symfony version(s) affected
7.2.5
Description
Today I noticed something weird with the Serializer. I have a
LoaderInterface
that defines a discriminator mapping dynamically. This works great.As soon as I start using the
#[Ignore]
attribute in an unrelated method, the discriminator is no longer added to the serialized result.I created a small isolated reproducer that explains the problem.
How to reproduce
Possible Solution
I have the feeling this is related to the
PropertyNormalizer
. When I remove that from the Serializer it works. But since this is a default normalizer, I'd like to keep this.Additional Context
No response
The text was updated successfully, but these errors were encountered: