Skip to content

Commit 43dc167

Browse files
committed
Supports nested "abstract" object while serializing and de-serializing
1 parent 31dd002 commit 43dc167

File tree

4 files changed

+75
-2
lines changed

4 files changed

+75
-2
lines changed

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ abstract protected function setAttributeValue($object, $attribute, $value, $form
294294
*/
295295
private function validateAndDenormalize(string $currentClass, string $attribute, $data, ?string $format, array $context)
296296
{
297-
if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($currentClass, $attribute)) {
297+
if (null === $types = $this->getTypes($currentClass, $attribute)) {
298298
return $data;
299299
}
300300

@@ -357,6 +357,20 @@ private function validateAndDenormalize(string $currentClass, string $attribute,
357357
throw new NotNormalizableValueException(sprintf('The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given).', $attribute, $currentClass, implode('", "', array_keys($expectedTypes)), gettype($data)));
358358
}
359359

360+
/**
361+
* Get the types for the given class and attribute.
362+
*
363+
* @return Type[]|null
364+
*/
365+
protected function getTypes(string $currentClass, string $attribute)
366+
{
367+
if (null === $this->propertyTypeExtractor) {
368+
return null;
369+
}
370+
371+
return $this->propertyTypeExtractor->getTypes($currentClass, $attribute);
372+
}
373+
360374
/**
361375
* Sets an attribute and apply the name converter if necessary.
362376
*

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\PropertyAccess\PropertyAccess;
1616
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
1717
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
18+
use Symfony\Component\PropertyInfo\Type;
1819
use Symfony\Component\Serializer\Exception\RuntimeException;
1920
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
2021
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
@@ -152,4 +153,30 @@ protected function getAllowedAttributes($classOrObject, array $context, $attribu
152153

153154
return $allowedAttributes;
154155
}
156+
157+
/**
158+
* {@inheritdoc}
159+
*/
160+
protected function getTypes(string $currentClass, string $attribute)
161+
{
162+
if (null !== $types = parent::getTypes($currentClass, $attribute)) {
163+
return $types;
164+
}
165+
166+
if (null !== $this->classDiscriminatorResolver && null !== $discriminatorMapping = $this->classDiscriminatorResolver->getMappingForClass($currentClass)) {
167+
if ($discriminatorMapping->getTypeProperty() === $attribute) {
168+
return array(
169+
new Type(Type::BUILTIN_TYPE_STRING),
170+
);
171+
}
172+
173+
foreach ($discriminatorMapping->getTypesMapping() as $mappedClass) {
174+
if (null !== $types = parent::getTypes($mappedClass, $attribute)) {
175+
return $types;
176+
}
177+
}
178+
}
179+
180+
return null;
181+
}
155182
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,18 @@
1616
*/
1717
class DummyMessageNumberTwo implements DummyMessageInterface
1818
{
19+
/**
20+
* @var DummyMessageNumberOne
21+
*/
22+
private $nested;
23+
24+
public function setNested(DummyMessageNumberOne $nested)
25+
{
26+
$this->nested = $nested;
27+
}
28+
29+
public function getNested(): DummyMessageNumberOne
30+
{
31+
return $this->nested;
32+
}
1933
}

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

Lines changed: 19 additions & 1 deletion
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\ReflectionExtractor;
1617
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
1718
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
1819
use Symfony\Component\Serializer\Mapping\ClassMetadata;
@@ -35,6 +36,7 @@
3536
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild;
3637
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface;
3738
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberOne;
39+
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberTwo;
3840
use Symfony\Component\Serializer\Tests\Fixtures\TraversableDummy;
3941
use Symfony\Component\Serializer\Tests\Fixtures\NormalizableTraversableDummy;
4042
use Symfony\Component\Serializer\Tests\Normalizer\TestNormalizer;
@@ -430,6 +432,22 @@ public function testDeserializeAndSerializeInterfacedObjectsWithTheClassMetadata
430432
$this->assertEquals('{"two":2,"type":"one"}', $serialized);
431433
}
432434

435+
public function testDeserializeAndSerializeNestedInterfacedObjectsWithTheClassMetadataDiscriminator()
436+
{
437+
$nested = new DummyMessageNumberOne();
438+
$nested->one = 'foo';
439+
440+
$example = new DummyMessageNumberTwo();
441+
$example->setNested($nested);
442+
443+
$serializer = $this->serializerWithClassDiscriminator();
444+
445+
$serialized = $serializer->serialize($example, 'json');
446+
$deserialized = $serializer->deserialize($serialized, DummyMessageInterface::class, 'json');
447+
448+
$this->assertEquals($example, $deserialized);
449+
}
450+
433451
/**
434452
* @expectedException \Symfony\Component\Serializer\Exception\RuntimeException
435453
* @expectedExceptionMessage The type "second" has no mapped class for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface"
@@ -452,7 +470,7 @@ private function serializerWithClassDiscriminator()
452470
{
453471
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
454472

455-
return new Serializer(array(new ObjectNormalizer($classMetadataFactory, null, null, null, new ClassDiscriminatorFromClassMetadata($classMetadataFactory))), array('json' => new JsonEncoder()));
473+
return new Serializer(array(new ObjectNormalizer($classMetadataFactory, null, null, new ReflectionExtractor(), new ClassDiscriminatorFromClassMetadata($classMetadataFactory))), array('json' => new JsonEncoder()));
456474
}
457475
}
458476

0 commit comments

Comments
 (0)