diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 9b9b9fb4fa243..5c708ae8a436b 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -329,6 +329,16 @@ public function denormalize($data, $type, $format = null, array $context = []) $object = $this->instantiateObject($normalizedData, $type, $context, $reflectionClass, $allowedAttributes, $format); $resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object); + if (\is_string($data) && 'xml' === $format && $this->classMetadataFactory->hasMetadataFor($type)) { + $attributes = $this->classMetadataFactory->getMetadataFor($type)->getAttributesMetadata(); + foreach ($attributes as $attribute) { + if ('#' === $attribute->getSerializedName()) { + $normalizedData = ['#' => $data]; + break; + } + } + } + foreach ($normalizedData as $attribute => $value) { if ($this->nameConverter) { $attribute = $this->nameConverter->denormalize($attribute, $resolvedClass, $format, $context); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index a45722423f8e8..0921df8ec7b94 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Type; +use Symfony\Component\Serializer\Annotation\SerializedName; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata; @@ -24,6 +25,7 @@ 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\NameConverter\MetadataAwareNameConverter; use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; @@ -227,6 +229,26 @@ public function hasMetadataFor($value): bool $this->assertInstanceOf(DummySecondChildQuux::class, $normalizedData->quux); } + public function testDenormalizeXmlStringNodeWithoutAttributesToObject() + { + $denormalizer = $this->getDenormalizerForStringNode(); + // if an xml-node can have children which should be deserialized as string[] + // and only one child exists + $object = $denormalizer->denormalize('string-value', DummyObjectWithOptionalAttributes::class, 'xml'); + $this->assertInstanceOf(DummyObjectWithOptionalAttributes::class, $object); + $this->assertEquals('string-value', $object->value); + $this->assertNull($object->foo); + } + + public function getDenormalizerForStringNode() + { + $denormalizer = new AbstractObjectNormalizerWithMetadataAndNameConverter(); + $serializer = new Serializer([$denormalizer]); + $denormalizer->setSerializer($serializer); + + return $denormalizer; + } + /** * Test that additional attributes throw an exception if no metadata factory is specified. */ @@ -302,6 +324,44 @@ class StringCollection public $children; } +class AbstractObjectNormalizerWithMetadataAndNameConverter extends AbstractObjectNormalizer +{ + public function __construct() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + parent::__construct($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory)); + } + + protected function extractAttributes($object, $format = null, array $context = []) + { + } + + protected function getAttributeValue($object, $attribute, $format = null, array $context = []) + { + } + + protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = []) + { + $object->$attribute = $value; + } +} + +class DummyObjectWithOptionalAttributes +{ + /** + * @SerializedName("#") + * + * @var string + */ + public $value; + /** + * @SerializedName("@foo") + * + * @var string + */ + public $foo = null; +} + class DummyCollection { /** @var DummyChild[] */