Skip to content

Commit 3b2f14b

Browse files
committed
feature #24375 [Serializer] Serialize and deserialize from abstract classes (sroze)
This PR was squashed before being merged into the 4.1-dev branch (closes #24375). Discussion ---------- [Serializer] Serialize and deserialize from abstract classes | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | ø | License | MIT | Doc PR | Not yet This PR adds a feature in the Serializer: allow to serialize and de-serialize abstract classes. Such feature is especially useful when dealing with domain objects. # Example Let's take the example of the following objects: - `CodeRepository` defines a set of properties like `name` and `url` - `GitHubCodeRepository` and `BitBucketCodeRepository` extends from the abstract `CodeRepository` class and adds a few properties. - `Project` has a relation with a `codeRepository`, which has a type `CodeRepository`. At the moment, the serializer can't serialize/deserialize correctly this `Project` object has it doesn't know how to deal with this `CodeRepository` abstract object. This feature allows the serializer to deal with such situation. The `ObjectNormalizer` has now access to a `ClassDiscriminatorResolver` that knows, for a given abstract class: - Is the "type" property it needs to read/write to uniquely identify each sub-class - What's the name of the "type" for each sub-class mapping # Usage without Framework Bundle ```php $discriminatorResolver = new ClassDiscriminatorResolver(); $discriminatorResolver->addClassMapping(CodeRepository::class, new ClassDiscriminatorMapping('type', [ 'github' => GitHubCodeRepository::class, 'bitbucket' => BitBucketCodeRepository::class, ])); $serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder())); $serialized = $serializer->serialize(new GitHubCodeRepository()); // {"type": "github"} $repository = $serializer->unserialize($serialized, CodeRepository::class, 'json'); // GitHubCodeRepository ``` # Usage with the Framework Bundle ```yaml framework: serializer: discriminator_class_mapping: App\CodeRepository: type_property: type mapping: github: App\GitHubCodeRepository bitbucket: App\BitBucketCodeRepository ``` # Usage with Annotations/XML/YAML ```php use Symfony\Component\Serializer\Annotation\DiscriminatorMap; /** * @DiscriminatorMap(typeProperty="type", mapping={ * "first"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild", * "second"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild" * }) */ abstract class AbstractDummy { public $foo; public function __construct($foo = null) { $this->foo = $foo; } } ``` # TODO - [x] Working as standalone - [x] Working with the framework bundle - [x] Tests on mapping classes Commits ------- 4c6e05b7ee [Serializer] Serialize and deserialize from abstract classes
2 parents fd9ba28 + ae16e96 commit 3b2f14b

File tree

2 files changed

+13
-0
lines changed

2 files changed

+13
-0
lines changed

DependencyInjection/FrameworkExtension.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
use Symfony\Component\Security\Core\Security;
6363
use Symfony\Component\Serializer\Encoder\DecoderInterface;
6464
use Symfony\Component\Serializer\Encoder\EncoderInterface;
65+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
6566
use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory;
6667
use Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer;
6768
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
@@ -1153,6 +1154,11 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
11531154
$container->removeDefinition('serializer.normalizer.dateinterval');
11541155
}
11551156

1157+
if (!class_exists(ClassDiscriminatorFromClassMetadata::class)) {
1158+
$container->removeAlias('Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface');
1159+
$container->removeDefinition('serializer.mapping.class_discriminator_resolver');
1160+
}
1161+
11561162
$chainLoader = $container->getDefinition('serializer.mapping.chain_loader');
11571163

11581164
if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccessor')) {

Resources/config/serializer.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424

2525
<service id="serializer.property_accessor" alias="property_accessor" />
2626

27+
<!-- Discriminator Map -->
28+
<service id="serializer.mapping.class_discriminator_resolver" class="Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata">
29+
<argument type="service" id="serializer.mapping.class_metadata_factory" />
30+
</service>
31+
<service id="Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface" alias="serializer.mapping.class_discriminator_resolver" />
32+
2733
<!-- Normalizer -->
2834
<service id="serializer.normalizer.dateinterval" class="Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer">
2935
<!-- Run before serializer.normalizer.object -->
@@ -50,6 +56,7 @@
5056
<argument>null</argument> <!-- name converter -->
5157
<argument type="service" id="serializer.property_accessor" />
5258
<argument type="service" id="property_info" on-invalid="ignore" />
59+
<argument type="service" id="serializer.mapping.class_discriminator_resolver" on-invalid="ignore" />
5360

5461
<!-- Run after all custom normalizers -->
5562
<tag name="serializer.normalizer" priority="-1000" />

0 commit comments

Comments
 (0)