bug #27773 [Serializer] Class discriminator and serialization groups (sroze)
This PR was merged into the 4.1 branch.
Discussion
----------
[Serializer] Class discriminator and serialization groups
| Q | A
| ------------- | ---
| Branch? | 4.1
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #27641
| License | MIT
| Doc PR | ø
It turns out the discriminator mapping does not work well with the serialization groups. This is fixing it (+ a little bit of cleaning in the tests).
Commits
-------
c91b7afe35
Ensure the class discriminator mechanism works with serialization groups as well
This commit is contained in:
commit
18aec2dba8
@ -16,6 +16,7 @@ use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\Serializer\Exception\RuntimeException;
|
||||
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
|
||||
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
|
||||
@ -131,4 +132,24 @@ class ObjectNormalizer extends AbstractObjectNormalizer
|
||||
// Properties not found are ignored
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false)
|
||||
{
|
||||
if (false === $allowedAttributes = parent::getAllowedAttributes($classOrObject, $context, $attributesAsString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null !== $this->classDiscriminatorResolver && null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForMappedObject($classOrObject)) {
|
||||
$allowedAttributes[] = $attributesAsString ? $discriminatorMapping->getTypeProperty() : new AttributeMetadata($discriminatorMapping->getTypeProperty());
|
||||
|
||||
foreach ($discriminatorMapping->getTypesMapping() as $class) {
|
||||
$allowedAttributes = array_merge($allowedAttributes, parent::getAllowedAttributes($class, $context, $attributesAsString));
|
||||
}
|
||||
}
|
||||
|
||||
return $allowedAttributes;
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||
|
||||
/**
|
||||
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||
* "first"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild",
|
||||
* "second"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild"
|
||||
* "one"="Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberOne",
|
||||
* "two"="Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberTwo"
|
||||
* })
|
||||
*
|
||||
* @author Samuel Roze <samuel.roze@gmail.com>
|
||||
|
@ -11,10 +11,17 @@
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
|
||||
/**
|
||||
* @author Samuel Roze <samuel.roze@gmail.com>
|
||||
*/
|
||||
class DummyMessageNumberOne implements DummyMessageInterface
|
||||
{
|
||||
public $one;
|
||||
|
||||
/**
|
||||
* @Groups({"two"})
|
||||
*/
|
||||
public $two;
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Samuel Roze <samuel.roze@gmail.com>
|
||||
*/
|
||||
class DummyMessageNumberTwo implements DummyMessageInterface
|
||||
{
|
||||
}
|
@ -11,11 +11,14 @@
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
|
||||
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
|
||||
use Symfony\Component\Serializer\Mapping\ClassMetadata;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
|
||||
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
@ -398,11 +401,9 @@ class SerializerTest extends TestCase
|
||||
$example = new DummyMessageNumberOne();
|
||||
$example->one = 1;
|
||||
|
||||
$jsonData = '{"message-type":"one","one":1}';
|
||||
|
||||
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($this->metadataFactoryMockForDummyInterface());
|
||||
$serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder()));
|
||||
$jsonData = '{"type":"one","one":1,"two":null}';
|
||||
|
||||
$serializer = $this->serializerWithClassDiscriminator();
|
||||
$deserialized = $serializer->deserialize($jsonData, DummyMessageInterface::class, 'json');
|
||||
$this->assertEquals($example, $deserialized);
|
||||
|
||||
@ -410,51 +411,48 @@ class SerializerTest extends TestCase
|
||||
$this->assertEquals($jsonData, $serialized);
|
||||
}
|
||||
|
||||
public function testDeserializeAndSerializeInterfacedObjectsWithTheClassMetadataDiscriminatorResolverAndGroups()
|
||||
{
|
||||
$example = new DummyMessageNumberOne();
|
||||
$example->two = 2;
|
||||
|
||||
$serializer = $this->serializerWithClassDiscriminator();
|
||||
$deserialized = $serializer->deserialize('{"type":"one","one":1,"two":2}', DummyMessageInterface::class, 'json', array(
|
||||
'groups' => array('two'),
|
||||
));
|
||||
|
||||
$this->assertEquals($example, $deserialized);
|
||||
|
||||
$serialized = $serializer->serialize($deserialized, 'json', array(
|
||||
'groups' => array('two'),
|
||||
));
|
||||
|
||||
$this->assertEquals('{"two":2,"type":"one"}', $serialized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Serializer\Exception\RuntimeException
|
||||
* @expectedExceptionMessage The type "second" has no mapped class for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface"
|
||||
*/
|
||||
public function testExceptionWhenTypeIsNotKnownInDiscriminator()
|
||||
{
|
||||
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($this->metadataFactoryMockForDummyInterface());
|
||||
$serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder()));
|
||||
$serializer->deserialize('{"message-type":"second","one":1}', DummyMessageInterface::class, 'json');
|
||||
$this->serializerWithClassDiscriminator()->deserialize('{"type":"second","one":1}', DummyMessageInterface::class, 'json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Serializer\Exception\RuntimeException
|
||||
* @expectedExceptionMessage Type property "message-type" not found for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface"
|
||||
* @expectedExceptionMessage Type property "type" not found for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface"
|
||||
*/
|
||||
public function testExceptionWhenTypeIsNotInTheBodyToDeserialiaze()
|
||||
{
|
||||
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($this->metadataFactoryMockForDummyInterface());
|
||||
$serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder()));
|
||||
$serializer->deserialize('{"one":1}', DummyMessageInterface::class, 'json');
|
||||
$this->serializerWithClassDiscriminator()->deserialize('{"one":1}', DummyMessageInterface::class, 'json');
|
||||
}
|
||||
|
||||
private function metadataFactoryMockForDummyInterface()
|
||||
private function serializerWithClassDiscriminator()
|
||||
{
|
||||
$factoryMock = $this->getMockBuilder(ClassMetadataFactoryInterface::class)->getMock();
|
||||
$factoryMock->method('hasMetadataFor')->will($this->returnValueMap(array(
|
||||
array(
|
||||
DummyMessageInterface::class,
|
||||
true,
|
||||
),
|
||||
)));
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
|
||||
$factoryMock->method('getMetadataFor')->will($this->returnValueMap(array(
|
||||
array(
|
||||
DummyMessageInterface::class,
|
||||
new ClassMetadata(
|
||||
DummyMessageInterface::class,
|
||||
new ClassDiscriminatorMapping('message-type', array(
|
||||
'one' => DummyMessageNumberOne::class,
|
||||
))
|
||||
),
|
||||
),
|
||||
)));
|
||||
|
||||
return $factoryMock;
|
||||
return new Serializer(array(new ObjectNormalizer($classMetadataFactory, null, null, null, new ClassDiscriminatorFromClassMetadata($classMetadataFactory))), array('json' => new JsonEncoder()));
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user