8000 [Serializer] AbstractObjectNormalizer ignores the property types of discriminated classes by sandergo90 · Pull Request #32383 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Serializer] AbstractObjectNormalizer ignores the property types of discriminated classes #32383

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

Merged
merged 1 commit into from
Jul 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
[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
  • Loading branch information
sandergo90 committed Jul 8, 2019
commit 9fc56c7e2836adc90e7739c1d46050e729912648
Original file line number Diff line number Diff line change
Expand Up @@ -281,21 +281,22 @@ public function denormalize($data, $class, $format = null, array $context = [])

$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;
}

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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,22 @@
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
{
Expand Down Expand Up @@ -147,6 +156,39 @@ private function getDenormalizerForDummyCollection()
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.
*
Expand Down Expand Up @@ -190,7 +232,7 @@ protected function setAttributeValue($object, $attribute, $value, $format = null

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)
Expand Down
7 changes: 5 additions & 2 deletions src/Symfony/Component/Serializer/Tests/SerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

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;
Expand All @@ -34,6 +35,7 @@
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;
Expand Down Expand Up @@ -382,6 +384,7 @@ public function testDeserializeObjectConstructorWithObjectTypeHint()
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([
Expand All @@ -405,9 +408,9 @@ public function testDeserializeAndSerializeAbstractObjectsWithTheClassMetadataDi
]);

$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);
Expand Down
0