Skip to content

Commit

Permalink
Merge d6ba320 into 785cd9c
Browse files Browse the repository at this point in the history
  • Loading branch information
alanpoulain committed Mar 22, 2021
2 parents 785cd9c + d6ba320 commit 19214c6
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,7 @@
## 2.6.4

* Serializer: Fix denormalization of basic property-types in XML and CSV (#3191)
* Serializer: Fix denormalization of collection with one element in XML (#4154)
* JSON Schema: Manage Sequentially and AtLeastOneOf constraints when generating property metadata (#4139 and #4147)
* Doctrine: Fix purging HTTP cache for unreadable relations (#3441)
* Doctrine: Revert #3774 support for binary UUID in search filter (#4134)
Expand Down
13 changes: 11 additions & 2 deletions src/Serializer/AbstractItemNormalizer.php
Expand Up @@ -727,9 +727,18 @@ private function createAttributeValue($attribute, $value, $format = null, array
return $value;
}

$collectionValueType = method_exists(Type::class, 'getCollectionValueTypes') ? ($type->getCollectionValueTypes()[0] ?? null) : $type->getCollectionValueType();

/* From @see AbstractObjectNormalizer::validateAndDenormalize() */
// Fix a collection that contains the only one element
// This is special to xml format only
if ('xml' === $format && null !== $collectionValueType && (!\is_array($value) || !\is_int(key($value)))) {
$value = [$value];
}

if (
$type->isCollection() &&
null !== ($collectionValueType = method_exists(Type::class, 'getCollectionValueTypes') ? ($type->getCollectionValueTypes()[0] ?? null) : $type->getCollectionValueType()) &&
null !== $collectionValueType &&
null !== ($className = $collectionValueType->getClassName()) &&
$this->resourceClassResolver->isResourceClass($className)
) {
Expand All @@ -752,7 +761,7 @@ private function createAttributeValue($attribute, $value, $format = null, array

if (
$type->isCollection() &&
null !== ($collectionValueType = method_exists(Type::class, 'getCollectionValueTypes') ? ($type->getCollectionValueTypes()[0] ?? null) : $type->getCollectionValueType()) &&
null !== $collectionValueType &&
null !== ($className = $collectionValueType->getClassName())
) {
if (!$this->serializer instanceof DenormalizerInterface) {
Expand Down
54 changes: 54 additions & 0 deletions tests/Serializer/AbstractItemNormalizerTest.php
Expand Up @@ -1301,6 +1301,60 @@ public function testDenormalizeBasicTypePropertiesFromXml()

$this->assertInstanceOf(ObjectWithBasicProperties::class, $objectWithBasicProperties);
}

public function testDenormalizeCollectionDecodedFromXmlWithOneChild()
{
$data = [
'relatedDummies' => [
'name' => 'foo',
],
];

$relatedDummy = new RelatedDummy();
$relatedDummy->setName('foo');

$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
$propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummies']));

$relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
$relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);

$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(new PropertyMetadata($relatedDummiesType, '', false, true, false, true));

$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);

$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
$propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummies', Argument::type('array'))->shouldBeCalled();

$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
$resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
$resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
$resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);

$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);
$serializerProphecy->denormalize(['name' => 'foo'], RelatedDummy::class, 'xml', Argument::type('array'))->willReturn($relatedDummy);

$normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal(),
$propertyAccessorProphecy->reveal(),
null,
null,
null,
false,
[],
[],
null,
null,
]);
$normalizer->setSerializer($serializerProphecy->reveal());

$normalizer->denormalize($data, Dummy::class, 'xml');
}
}

class ObjectWithBasicProperties
Expand Down

0 comments on commit 19214c6

Please sign in to comment.