8000 [Serializer]: AbstractObjectNormalizer ignores the property types of … · symfony/symfony@9fc56c7 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9fc56c7

Browse files
committed
[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
1 parent e30800d commit 9fc56c7

File tree

7 files changed

+138
-6
lines changed

7 files changed

+138
-6
lines changed

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,21 +281,22 @@ public function denormalize($data, $class, $format = null, array $context = [])
281281

282282
$reflectionClass = new \ReflectionClass($class);
283283
$object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format);
284+
$resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object);
284285

285286
foreach ($normalizedData as $attribute => $value) {
286287
if ($this->nameConverter) {
287-
$attribute = $this->nameConverter->denormalize($attribute, $class, $format, $context);
288+
$attribute = $this->nameConverter->denormalize($attribute, $resolvedClass, $format, $context);
288289
}
289290

290-
if ((false !== $allowedAttributes && !\in_array($attribute, $allowedAttributes)) || !$this->isAllowedAttribute($class, $attribute, $format, $context)) {
291+
if ((false !== $allowedAttributes && !\in_array($attribute, $allowedAttributes)) || !$this->isAllowedAttribute($resolvedClass, $attribute, $format, $context)) {
291292
if (!($context[self::ALLOW_EXTRA_ATTRIBUTES] ?? $this->defaultContext[self::ALLOW_EXTRA_ATTRIBUTES])) {
292293
$extraAttributes[] = $attribute;
293294
}
294295

295296
continue;
296297
}
297298

298-
$value = $this->validateAndDenormalize($class, $attribute, $value, $format, $context);
299+
$value = $this->validateAndDenormalize($resolvedClass, $attribute, $value, $format, $context);
299300
try {
300301
$this->setAttributeValue($object, $attribute, $value, $format, $context);
301302
} catch (InvalidArgumentException $e) {

src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummyFirstChild.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,23 @@ class AbstractDummyFirstChild extends AbstractDummy
1515
{
1616
public $bar;
1717

18+
/** @var DummyFirstChildQuux|null */
19+
public $quux;
20+
1821
public function __construct($foo = null, $bar = null)
1922
{
2023
parent::__construct($foo);
2124

2225
$this->bar = $bar;
2326
}
27+
28+
public function getQuux(): ?DummyFirstChildQuux
29+
{
30+
return $this->quux;
31+
}
32+
33+
public function setQuux(DummyFirstChildQuux $quux): void
34+
{
35+
$this->quux = $quux;
36+
}
2437
}

src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummySecondChild.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,23 @@ class AbstractDummySecondChild extends AbstractDummy
1515
{
1616
public $baz;
1717

18+
/** @var DummySecondChildQuux|null */
19+
public $quux;
20+
1821
public function __construct($foo = null, $baz = null)
1922
{
2023
parent::__construct($foo);
2124

2225
$this->baz = $baz;
2326
}
27+
28+
public function getQuux(): ?DummySecondChildQuux
29+
{
30+
return $this->quux;
31+
}
32+
33+
public function setQuux(DummySecondChildQuux $quux): void
34+
{
35+
$this->quux = $quux;
36+
}
2437
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Tests\Fixtures;
13+
14+
class DummyFirstChildQuux
15+
{
16+
/**
17+
* @var string
18+
*/
19+
private $value;
20+
21+
public function __construct(string $value)
22+
{
23+
$this->value = $value;
24+
}
25+
26+
public function getValue(): string
27+
{
28+
return $this->value;
29+
}
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Tests\Fixtures;
13+
14+
class DummySecondChildQuux
15+
{
16+
/**
17+
* @var string
18+
*/
19+
private $value;
20+
21+
public function __construct(string $value)
22+
{
23+
$this->value = $value;
24+
}
25+
26+
public function getValue(): string
27+
{
28+
return $this->value;
29+
}
30+
}

src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,22 @@
1616
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
1717
use Symfony\Component\PropertyInfo\Type;
1818
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
19+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
20+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
21+
use Symfony\Component\Serializer\Mapping\ClassMetadata;
1922
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
23+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
2024
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
2125
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
2226
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
2327
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
28+
use Symfony\Component\Serializer\Serializer;
2429
use Symfony\Component\Serializer\SerializerAwareInterface;
2530
use Symfony\Component\Serializer\SerializerInterface;
31+
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy;
32+
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild;
33+
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild;
34+
use Symfony\Component\Serializer\Tests\Fixtures\DummySecondChildQuux;
2635

2736
class AbstractObjectNormalizerTest extends TestCase
2837
{
@@ -147,6 +156,39 @@ private function getDenormalizerForDummyCollection()
147156
return $denormalizer;
148157
}
149158

159+
public function testDenormalizeWithDiscriminatorMapUsesCorrectClassname()
160+
{
161+
$factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
162+
$loaderMock = $this->getMockBuilder(ClassMetadataFactoryInterface::class)->getMock();
163+
$loaderMock->method('hasMetadataFor')->willReturnMap([
164+
[
165+
AbstractDummy::class,
166+
true,
167+
],
168+
]);
169+
170+
$loaderMock->method('getMetadataFor')->willReturnMap([
171+
[
172+
AbstractDummy::class,
173+
new ClassMetadata(
174+
AbstractDummy::class,
175+
new ClassDiscriminatorMapping('type', [
176+
'first' => AbstractDummyFirstChild::class,
177+
'second' => AbstractDummySecondChild::class,
178+
])
179+
),
180+
],
181+
]);
182+
183+
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock);
184+
$normalizer = new AbstractObjectNormalizerDummy($factory, null, new PhpDocExtractor(), $discriminatorResolver);
185+
$serializer = new Serializer([$normalizer]);
186+
$normalizer->setSerializer($serializer);
187+
$normalizedData = $normalizer->denormalize(['foo' => 'foo', 'baz' => 'baz', 'quux' => ['value' => 'quux'], 'type' => 'second'], AbstractDummy::class);
188+
189+
$this->assertInstanceOf(DummySecondChildQuux::class, $normalizedData->quux);
190+
}
191+
15 F438 0192
/**
151193
* Test that additional attributes throw an exception if no metadata factory is specified.
152194
*
@@ -190,7 +232,7 @@ protected function setAttributeValue($object, $attribute, $value, $format = null
190232

191233
protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = [])
192234
{
193-
return \in_array($attribute, ['foo', 'baz']);
235+
return \in_array($attribute, ['foo', 'baz', 'quux', 'value']);
194236
}
195237

196238
public function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, string $format = null)

src/Symfony/Component/Serializer/Tests/SerializerTest.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Doctrine\Common\Annotations\AnnotationReader;
1515
use PHPUnit\Framework\TestCase;
16+
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
1617
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
1718
use Symfony\Component\Serializer\Encoder\JsonEncoder;
1819
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
@@ -34,6 +35,7 @@
3435
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy;
3536
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild;
3637
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild;
38+
use Symfony\Component\Serializer\Tests\Fixtures\DummyFirstChildQuux;
3739
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface;
3840
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberOne;
3941
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberTwo;
@@ -382,6 +384,7 @@ public function testDeserializeObjectConstructorWithObjectTypeHint()
382384
public function testDeserializeAndSerializeAbstractObjectsWithTheClassMetadataDiscriminatorResolver()
383385
{
384386
$example = new AbstractDummyFirstChild('foo-value', 'bar-value');
387+
$example->setQuux(new DummyFirstChildQuux('quux'));
385388

386389
$loaderMock = $this->getMockBuilder(ClassMetadataFactoryInterface::class)->getMock();
387390
$loaderMock->method('hasMetadataFor')->willReturnMap([
@@ -405,9 +408,9 @@ public function testDeserializeAndSerializeAbstractObjectsWithTheClassMetadataDi
405408
]);
406409

407410
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock);
408-
$serializer = new Serializer([new ObjectNormalizer(null, null, null, null, $discriminatorResolver)], ['json' => new JsonEncoder()]);
411+
$serializer = new Serializer([new ObjectNormalizer(null, null, null, new PhpDocExtractor(), $discriminatorResolver)], ['json' => new JsonEncoder()]);
409412

410-
$jsonData = '{"type":"first","bar":"bar-value","foo":"foo-value"}';
413+
$jsonData = '{"type":"first","quux":{"value":"quux"},"bar":"bar-value","foo":"foo-value"}';
411414

412415
$deserialized = $serializer->deserialize($jsonData, AbstractDummy::class, 'json');
413416
$this->assertEquals($example, $deserialized);

0 commit comments

Comments
 (0)
0