Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Serializer] deserialize from xml: Fix a collection that contains the only one element #27326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,12 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma
$builtinType = Type::BUILTIN_TYPE_OBJECT;
$class = $collectionValueType->getClassName().'[]';

// Fix a collection that contains the only one element
// This is special to xml format only
if ('xml' === $format && !is_int(key($data))) {
$data = array($data);
}

if (null !== $collectionKeyType = $type->getCollectionKeyType()) {
$context['key_type'] = $collectionKeyType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@

use Doctrine\Common\Annotations\AnnotationReader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerInterface;

class AbstractObjectNormalizerTest extends TestCase
{
Expand Down Expand Up @@ -69,6 +73,75 @@ public function testDenormalizeWithExtraAttributesAndNoGroupsWithMetadataFactory
array('allow_extra_attributes' => false)
);
}

public function testDenormalizeCollectionDecodedFromXmlWithOneChild()
{
$denormalizer = $this->getDenormalizerForDummyCollection();

$dummyCollection = $denormalizer->denormalize(
array(
'children' => array(
'bar' => 'first',
),
),
DummyCollection::class,
'xml'
);

$this->assertInstanceOf(DummyCollection::class, $dummyCollection);
$this->assertInternalType('array', $dummyCollection->children);
$this->assertCount(1, $dummyCollection->children);
$this->assertInstanceOf(DummyChild::class, $dummyCollection->children[0]);
}

public function testDenormalizeCollectionDecodedFromXmlWithTwoChildren()
{
$denormalizer = $this->getDenormalizerForDummyCollection();

$dummyCollection = $denormalizer->denormalize(
array(
'children' => array(
array('bar' => 'first'),
array('bar' => 'second'),
),
),
DummyCollection::class,
'xml'
);

$this->assertInstanceOf(DummyCollection::class, $dummyCollection);
$this->assertInternalType('array', $dummyCollection->children);
$this->assertCount(2, $dummyCollection->children);
$this->assertInstanceOf(DummyChild::class, $dummyCollection->children[0]);
$this->assertInstanceOf(DummyChild::class, $dummyCollection->children[1]);
}

private function getDenormalizerForDummyCollection()
{
$extractor = $this->getMockBuilder('Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor')->getMock();
$extractor->method('getTypes')
->will($this->onConsecutiveCalls(
array(
new \Symfony\Component\PropertyInfo\Type(
'array',
false,
null,
true,
new \Symfony\Component\PropertyInfo\Type('int'),
new \Symfony\Component\PropertyInfo\Type('object', false, DummyChild::class)
),
),
null
));

$denormalizer = new AbstractObjectNormalizerCollectionDummy(null, null, $extractor);
$arrayDenormalizer = new ArrayDenormalizerDummy();
$serializer = new SerializerCollectionDummy(array($arrayDenormalizer, $denormalizer));
$arrayDenormalizer->setSerializer($serializer);
$denormalizer->setSerializer($serializer);

return $denormalizer;
}
}

class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer
Expand Down Expand Up @@ -124,3 +197,126 @@ protected function setAttributeValue($object, $attribute, $value, $format = null
$object->$attribute = $value;
}
}

class DummyCollection
{
/** @var DummyChild[] */
public $children;
}

class DummyChild
{
public $bar;
}

class SerializerCollectionDummy implements \Symfony\Component\Serializer\SerializerInterface, \Symfony\Component\Serializer\Normalizer\DenormalizerInterface
{
/** @var \Symfony\Component\Serializer\Normalizer\DenormalizerInterface */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please review 24b6848 and report any mistake

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me

private $normalizers;

/**
* @param $normalizers
*/
public function __construct($normalizers)
{
$this->normalizers = $normalizers;
}

public function serialize($data, $format, array $context = array())
{
}

public function deserialize($data, $type, $format, array $context = array())
{
}

public function denormalize($data, $type, $format = null, array $context = array())
{
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof \Symfony\Component\Serializer\Normalizer\DenormalizerInterface && $normalizer->supportsDenormalization($data, $type, $format, $context)) {
return $normalizer->denormalize($data, $type, $format, $context);
}
}
}

public function supportsDenormalization($data, $type, $format = null)
{
return true;
}
}

class AbstractObjectNormalizerCollectionDummy extends \Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer
{
protected function extractAttributes($object, $format = null, array $context = array())
{
}

protected function getAttributeValue($object, $attribute, $format = null, array $context = array())
{
}

protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array())
{
$object->$attribute = $value;
}

protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = array())
{
return true;
}

public function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, $format = null)
{
return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes, $format);
}

public function serialize($data, $format, array $context = array())
{
}

public function deserialize($data, $type, $format, array $context = array())
{
}
}

class ArrayDenormalizerDummy implements DenormalizerInterface, SerializerAwareInterface
{
/**
* @var SerializerInterface|DenormalizerInterface
*/
private $serializer;

/**
* {@inheritdoc}
*
* @throws NotNormalizableValueException
*/
public function denormalize($data, $class, $format = null, array $context = array())
{
$serializer = $this->serializer;
$class = substr($class, 0, -2);

foreach ($data as $key => $value) {
$data[$key] = $serializer->denormalize($value, $class, $format, $context);
}

return $data;
}

/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null, array $context = array())
{
return '[]' === substr($type, -2)
&& $this->serializer->supportsDenormalization($data, substr($type, 0, -2), $format, $context);
}

/**
* {@inheritdoc}
*/
public function setSerializer(SerializerInterface $serializer)
{
$this->serializer = $serializer;
}
}