Skip to content

Commit 16c325c

Browse files
committed
[Serializer] fix denormalization of xml-values to objects when no (optional) attributes are present #32144
1 parent 3df1d1e commit 16c325c

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

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

+10
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,16 @@ public function denormalize($data, $type, $format = null, array $context = [])
329329
$object = $this->instantiateObject($normalizedData, $type, $context, $reflectionClass, $allowedAttributes, $format);
330330
$resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object);
331331

332+
if (\is_string($data) && 'xml' === $format && $this->classMetadataFactory->hasMetadataFor($type)) {
333+
$attributes = $this->classMetadataFactory->getMetadataFor($type)->getAttributesMetadata();
334+
foreach ($attributes as $attribute) {
335+
if ('#' === $attribute->getSerializedName()) {
336+
$normalizedData = ['#' => $data];
337+
break;
338+
}
339+
}
340+
}
341+
332342
foreach ($normalizedData as $attribute => $value) {
333343
if ($this->nameConverter) {
334344
$attribute = $this->nameConverter->denormalize($attribute, $resolvedClass, $format, $context);

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

+60
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PHPUnit\Framework\TestCase;
1616
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
1717
use Symfony\Component\PropertyInfo\Type;
18+
use Symfony\Component\Serializer\Annotation\SerializedName;
1819
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
1920
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
2021
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
@@ -24,6 +25,7 @@
2425
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
2526
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
2627
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
28+
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;
2729
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
2830
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
2931
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
@@ -227,6 +229,26 @@ public function hasMetadataFor($value): bool
227229
$this->assertInstanceOf(DummySecondChildQuux::class, $normalizedData->quux);
228230
}
229231

232+
public function testDenormalizeXmlStringNodeWithoutAttributesToObject()
233+
{
234+
$denormalizer = $this->getDenormalizerForStringNode();
235+
// if an xml-node can have children which should be deserialized as string[]
236+
// and only one child exists
237+
$object = $denormalizer->denormalize('string-value', DummyObjectWithOptionalAttributes::class, 'xml');
238+
$this->assertInstanceOf(DummyObjectWithOptionalAttributes::class, $object);
239+
$this->assertEquals('string-value', $object->value);
240+
$this->assertNull($object->foo);
241+
}
242+
243+
public function getDenormalizerForStringNode()
244+
{
245+
$denormalizer = new AbstractObjectNormalizerWithMetadataAndNameConverter();
246+
$serializer = new Serializer([$denormalizer]);
247+
$denormalizer->setSerializer($serializer);
248+
249+
return $denormalizer;
250+
}
251+
230252
/**
231253
* Test that additional attributes throw an exception if no metadata factory is specified.
232254
*/
@@ -302,6 +324,44 @@ class StringCollection
302324
public $children;
303325
}
304326

327+
class AbstractObjectNormalizerWithMetadataAndNameConverter extends AbstractObjectNormalizer
328+
{
329+
public function __construct()
330+
{
331+
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
332+
parent::__construct($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory));
333+
}
334+
335+
protected function extractAttributes($object, $format = null, array $context = [])
336+
{
337+
}
338+
339+
protected function getAttributeValue($object, $attribute, $format = null, array $context = [])
340+
{
341+
}
342+
343+
protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = [])
344+
{
345+
$object->$attribute = $value;
346+
}
347+
}
348+
349+
class DummyObjectWithOptionalAttributes
350+
{
351+
/**
352+
* @SerializedName("#")
353+
*
354+
* @var string
355+
*/
356+
public $value;
357+
/**
358+
* @SerializedName("@foo")
359+
*
360+
* @var string
361+
*/
362+
public $foo = null;
363+
}
364+
305365
class DummyCollection
306366
{
307367
/** @var DummyChild[] */

0 commit comments

Comments
 (0)