Skip to content

Commit b80705a

Browse files
committed
[Serializer] Allow (de)normalization of empty objects in PropertyNormalizer and GetSetMethodNormalizer
1 parent 6fef5b4 commit b80705a

File tree

4 files changed

+111
-0
lines changed

4 files changed

+111
-0
lines changed

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111

1212
namespace Symfony\Component\Serializer\Normalizer;
1313

14+
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
15+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
16+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
17+
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
18+
1419
/**
1520
* Converts between objects with getter and setter methods and arrays.
1621
*
@@ -36,6 +41,21 @@ class GetSetMethodNormalizer extends AbstractObjectNormalizer
3641
{
3742
private static $setterAccessibleCache = [];
3843

44+
private $allowNormalizationOfObjectsWithoutAnyGetters;
45+
46+
public function __construct(
47+
ClassMetadataFactoryInterface $classMetadataFactory = null,
48+
NameConverterInterface $nameConverter = null,
49+
PropertyTypeExtractorInterface $propertyTypeExtractor = null,
50+
ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null,
51+
callable $objectClassResolver = null,
52+
array $defaultContext = [],
53+
bool $allowNormalizationOfObjectsWithoutAnyGetters = false,
54+
) {
55+
parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor, $classDiscriminatorResolver, $objectClassResolver, $defaultContext);
56+
$this->allowNormalizationOfObjectsWithoutAnyGetters = $allowNormalizationOfObjectsWithoutAnyGetters;
57+
}
58+
3959
/**
4060
* {@inheritdoc}
4161
*
@@ -69,6 +89,10 @@ public function hasCacheableSupportsMethod(): bool
6989
*/
7090
private function supports(string $class): bool
7191
{
92+
if ($this->allowNormalizationOfObjectsWithoutAnyGetters) {
93+
return true;
94+
}
95+
7296
$class = new \ReflectionClass($class);
7397
$methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
7498
foreach ($methods as $method) {

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
namespace Symfony\Component\Serializer\Normalizer;
1313

1414
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
15+
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
16+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
17+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
18+
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
1519

1620
/**
1721
* Converts between objects and arrays by mapping properties.
@@ -32,6 +36,21 @@
3236
*/
3337
class PropertyNormalizer extends AbstractObjectNormalizer
3438
{
39+
private $allowNormalizationOfObjectsWithoutAnyProperties;
40+
41+
public function __construct(
42+
ClassMetadataFactoryInterface $classMetadataFactory = null,
43+
NameConverterInterface $nameConverter = null,
44+
PropertyTypeExtractorInterface $propertyTypeExtractor = null,
45+
ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null,
46+
callable $objectClassResolver = null,
47+
array $defaultContext = [],
48+
bool $allowNormalizationOfObjectsWithoutAnyProperties = false,
49+
) {
50+
parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor, $classDiscriminatorResolver, $objectClassResolver, $defaultContext);
51+
$this->allowNormalizationOfObjectsWithoutAnyProperties = $allowNormalizationOfObjectsWithoutAnyProperties;
52+
}
53+
3554
/**
3655
* {@inheritdoc}
3756
*
@@ -65,6 +84,10 @@ public function hasCacheableSupportsMethod(): bool
6584
*/
6685
private function supports(string $class): bool
6786
{
87+
if ($this->allowNormalizationOfObjectsWithoutAnyProperties) {
88+
return true;
89+
}
90+
6891
$class = new \ReflectionClass($class);
6992

7093
// We look for at least one non-static property

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,36 @@ public function testRejectInvalidKey()
357357
$this->markTestSkipped('This test makes no sense with the GetSetMethodNormalizer');
358358
}
359359

360+
protected function getNormalizerAllowingObjectsWithoutGetters(): GetSetMethodNormalizer
361+
{
362+
return new GetSetMethodNormalizer(null, null, null, null, null, [], true);
363+
}
364+
365+
public function testNormalizeObjectWithoutAnyProperties()
366+
{
367+
$normalizer = $this->getNormalizerAllowingObjectsWithoutGetters();
368+
$obj = new EmptyObjectDummy();
369+
370+
$this->assertTrue($normalizer->supportsNormalization($obj));
371+
372+
$this->assertEquals(
373+
[],
374+
$normalizer->normalize($obj),
375+
);
376+
}
377+
378+
public function testDenormalizeObjectWithoutAnyProperties()
379+
{
380+
$normalizer = $this->getNormalizerAllowingObjectsWithoutGetters();
381+
$obj = new EmptyObjectDummy();
382+
383+
$this->assertTrue($normalizer->supportsDenormalization($obj, \get_class($obj)));
384+
$this->assertEquals(
385+
$obj,
386+
$normalizer->denormalize([], \get_class($obj)),
387+
);
388+
}
389+
360390
protected function getNormalizerForIgnoredAttributes(): GetSetMethodNormalizer
361391
{
362392
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
@@ -722,3 +752,7 @@ public function hasFoo()
722752
return $this->foo;
723753
}
724754
}
755+
756+
class EmptyObjectDummy
757+
{
758+
}

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,36 @@ protected function getDenormalizerForGroups(): PropertyNormalizer
246246
return new PropertyNormalizer($classMetadataFactory);
247247
}
248248

249+
protected function getNormalizerAllowingObjectsWithoutProperties(): PropertyNormalizer
250+
{
251+
return new PropertyNormalizer(null, null, null, null, null, [], true);
252+
}
253+
254+
public function testNormalizeObjectWithoutAnyProperties()
255+
{
256+
$normalizer = $this->getNormalizerAllowingObjectsWithoutProperties();
257+
$obj = new StaticPropertyDummy();
258+
259+
$this->assertTrue($normalizer->supportsNormalization($obj));
260+
261+
$this->assertEquals(
262+
[],
263+
$normalizer->normalize($obj),
264+
);
265+
}
266+
267+
public function testDenormalizeObjectWithoutAnyProperties()
268+
{
269+
$normalizer = $this->getNormalizerAllowingObjectsWithoutProperties();
270+
$obj = new StaticPropertyDummy();
271+
272+
$this->assertTrue($normalizer->supportsDenormalization($obj, \get_class($obj)));
273+
$this->assertEquals(
274+
$obj,
275+
$normalizer->denormalize([], \get_class($obj)),
276+
);
277+
}
278+
249279
public function testGroupsNormalizeWithNameConverter()
250280
{
251281
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));

0 commit comments

Comments
 (0)