[Serializer]: AbstractObjectNormalizer ignores the property types of discriminated classes
Add tests Remove test group Allow null Add quux null attribute Add quux value to serialize test
This commit is contained in:
parent
e30800df2c
commit
9fc56c7e28
@ -281,13 +281,14 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
||||
|
||||
$reflectionClass = new \ReflectionClass($class);
|
||||
$object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format);
|
||||
$resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object);
|
||||
|
||||
foreach ($normalizedData as $attribute => $value) {
|
||||
if ($this->nameConverter) {
|
||||
$attribute = $this->nameConverter->denormalize($attribute, $class, $format, $context);
|
||||
$attribute = $this->nameConverter->denormalize($attribute, $resolvedClass, $format, $context);
|
||||
}
|
||||
|
||||
if ((false !== $allowedAttributes && !\in_array($attribute, $allowedAttributes)) || !$this->isAllowedAttribute($class, $attribute, $format, $context)) {
|
||||
if ((false !== $allowedAttributes && !\in_array($attribute, $allowedAttributes)) || !$this->isAllowedAttribute($resolvedClass, $attribute, $format, $context)) {
|
||||
if (!($context[self::ALLOW_EXTRA_ATTRIBUTES] ?? $this->defaultContext[self::ALLOW_EXTRA_ATTRIBUTES])) {
|
||||
$extraAttributes[] = $attribute;
|
||||
}
|
||||
@ -295,7 +296,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $this->validateAndDenormalize($class, $attribute, $value, $format, $context);
|
||||
$value = $this->validateAndDenormalize($resolvedClass, $attribute, $value, $format, $context);
|
||||
try {
|
||||
$this->setAttributeValue($object, $attribute, $value, $format, $context);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
|
@ -15,10 +15,23 @@ class AbstractDummyFirstChild extends AbstractDummy
|
||||
{
|
||||
public $bar;
|
||||
|
||||
/** @var DummyFirstChildQuux|null */
|
||||
public $quux;
|
||||
|
||||
public function __construct($foo = null, $bar = null)
|
||||
{
|
||||
parent::__construct($foo);
|
||||
|
||||
$this->bar = $bar;
|
||||
}
|
||||
|
||||
public function getQuux(): ?DummyFirstChildQuux
|
||||
{
|
||||
return $this->quux;
|
||||
}
|
||||
|
||||
public function setQuux(DummyFirstChildQuux $quux): void
|
||||
{
|
||||
$this->quux = $quux;
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,23 @@ class AbstractDummySecondChild extends AbstractDummy
|
||||
{
|
||||
public $baz;
|
||||
|
||||
/** @var DummySecondChildQuux|null */
|
||||
public $quux;
|
||||
|
||||
public function __construct($foo = null, $baz = null)
|
||||
{
|
||||
parent::__construct($foo);
|
||||
|
||||
$this->baz = $baz;
|
||||
}
|
||||
|
||||
public function getQuux(): ?DummySecondChildQuux
|
||||
{
|
||||
return $this->quux;
|
||||
}
|
||||
|
||||
public function setQuux(DummySecondChildQuux $quux): void
|
||||
{
|
||||
$this->quux = $quux;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
<?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;
|
||||
|
||||
class DummyFirstChildQuux
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $value;
|
||||
|
||||
public function __construct(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<?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;
|
||||
|
||||
class DummySecondChildQuux
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $value;
|
||||
|
||||
public function __construct(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
@ -16,13 +16,22 @@ use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
|
||||
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\AbstractObjectNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Serializer\SerializerAwareInterface;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\DummySecondChildQuux;
|
||||
|
||||
class AbstractObjectNormalizerTest extends TestCase
|
||||
{
|
||||
@ -147,6 +156,39 @@ class AbstractObjectNormalizerTest extends TestCase
|
||||
return $denormalizer;
|
||||
}
|
||||
|
||||
public function testDenormalizeWithDiscriminatorMapUsesCorrectClassname()
|
||||
{
|
||||
$factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$loaderMock = $this->getMockBuilder(ClassMetadataFactoryInterface::class)->getMock();
|
||||
$loaderMock->method('hasMetadataFor')->willReturnMap([
|
||||
[
|
||||
AbstractDummy::class,
|
||||
true,
|
||||
],
|
||||
]);
|
||||
|
||||
$loaderMock->method('getMetadataFor')->willReturnMap([
|
||||
[
|
||||
AbstractDummy::class,
|
||||
new ClassMetadata(
|
||||
AbstractDummy::class,
|
||||
new ClassDiscriminatorMapping('type', [
|
||||
'first' => AbstractDummyFirstChild::class,
|
||||
'second' => AbstractDummySecondChild::class,
|
||||
])
|
||||
),
|
||||
],
|
||||
]);
|
||||
|
||||
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock);
|
||||
$normalizer = new AbstractObjectNormalizerDummy($factory, null, new PhpDocExtractor(), $discriminatorResolver);
|
||||
$serializer = new Serializer([$normalizer]);
|
||||
$normalizer->setSerializer($serializer);
|
||||
$normalizedData = $normalizer->denormalize(['foo' => 'foo', 'baz' => 'baz', 'quux' => ['value' => 'quux'], 'type' => 'second'], AbstractDummy::class);
|
||||
|
||||
$this->assertInstanceOf(DummySecondChildQuux::class, $normalizedData->quux);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that additional attributes throw an exception if no metadata factory is specified.
|
||||
*
|
||||
@ -190,7 +232,7 @@ class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer
|
||||
|
||||
protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = [])
|
||||
{
|
||||
return \in_array($attribute, ['foo', 'baz']);
|
||||
return \in_array($attribute, ['foo', 'baz', 'quux', 'value']);
|
||||
}
|
||||
|
||||
public function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, string $format = null)
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Serializer\Tests;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
|
||||
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
|
||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
||||
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
|
||||
@ -34,6 +35,7 @@ use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\DummyFirstChildQuux;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberOne;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberTwo;
|
||||
@ -382,6 +384,7 @@ class SerializerTest extends TestCase
|
||||
public function testDeserializeAndSerializeAbstractObjectsWithTheClassMetadataDiscriminatorResolver()
|
||||
{
|
||||
$example = new AbstractDummyFirstChild('foo-value', 'bar-value');
|
||||
$example->setQuux(new DummyFirstChildQuux('quux'));
|
||||
|
||||
$loaderMock = $this->getMockBuilder(ClassMetadataFactoryInterface::class)->getMock();
|
||||
$loaderMock->method('hasMetadataFor')->willReturnMap([
|
||||
@ -405,9 +408,9 @@ class SerializerTest extends TestCase
|
||||
]);
|
||||
|
||||
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock);
|
||||
$serializer = new Serializer([new ObjectNormalizer(null, null, null, null, $discriminatorResolver)], ['json' => new JsonEncoder()]);
|
||||
$serializer = new Serializer([new ObjectNormalizer(null, null, null, new PhpDocExtractor(), $discriminatorResolver)], ['json' => new JsonEncoder()]);
|
||||
|
||||
$jsonData = '{"type":"first","bar":"bar-value","foo":"foo-value"}';
|
||||
$jsonData = '{"type":"first","quux":{"value":"quux"},"bar":"bar-value","foo":"foo-value"}';
|
||||
|
||||
$deserialized = $serializer->deserialize($jsonData, AbstractDummy::class, 'json');
|
||||
$this->assertEquals($example, $deserialized);
|
||||
|
Reference in New Issue
Block a user