Support for array denormalization.

This commit is contained in:
Alexander M. Turek 2015-04-13 23:07:23 +02:00
parent a57ce907be
commit 0573f28d9b
7 changed files with 268 additions and 2 deletions

View File

@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Serializer\Exception;
class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface
{
}

View File

@ -0,0 +1,77 @@
<?php
namespace Symfony\Component\Serializer\Normalizer;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Component\Serializer\Exception\BadMethodCallException;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Denormalizes arrays of objects.
*
* @author Alexander M. Turek <me@derrabus.de>
*/
class ArrayDenormalizer implements DenormalizerInterface, SerializerAwareInterface
{
/**
* @var SerializerInterface|DenormalizerInterface
*/
private $serializer;
/**
* {@inheritdoc}
*/
public function denormalize($data, $class, $format = null, array $context = array())
{
if ($this->serializer === null) {
throw new BadMethodCallException('Please set a serializer before calling denormalize()!');
}
if (!is_array($data)) {
throw new InvalidArgumentException('Data expected to be an array, '.gettype($data).' given.');
}
if (substr($class, -2) !== '[]') {
throw new InvalidArgumentException('Unsupported class: '.$class);
}
$serializer = $this->serializer;
$class = substr($class, 0, -2);
return array_map(
function ($data) use ($serializer, $class, $format, $context) {
return $serializer->denormalize($data, $class, $format, $context);
},
$data
);
}
/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null)
{
return substr($type, -2) === '[]'
&& $this->serializer->supportsDenormalization($data, substr($type, 0, -2), $format);
}
/**
* {@inheritdoc}
*/
public function setSerializer(SerializerInterface $serializer)
{
if (!$serializer instanceof DenormalizerInterface) {
throw new InvalidArgumentException('Expected a serializer that also implements DenormalizerInterface.');
}
$this->serializer = $serializer;
}
}

View File

@ -59,6 +59,10 @@ class CustomNormalizer extends SerializerAwareNormalizer implements NormalizerIn
*/
public function supportsDenormalization($data, $type, $format = null)
{
if (!class_exists($type)) {
return false;
}
$class = new \ReflectionClass($type);
return $class->isSubclassOf('Symfony\Component\Serializer\Normalizer\DenormalizableInterface');

View File

@ -135,7 +135,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
*/
public function supportsDenormalization($data, $type, $format = null)
{
return $this->supports($type);
return class_exists($type) && $this->supports($type);
}
/**

View File

@ -135,7 +135,7 @@ class PropertyNormalizer extends AbstractNormalizer
*/
public function supportsDenormalization($data, $type, $format = null)
{
return $this->supports($type);
return class_exists($type) && $this->supports($type);
}
/**

View File

@ -0,0 +1,121 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Serializer\Tests\Normalizer;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\SerializerInterface;
class ArrayDenormalizerTest extends \PHPUnit_Framework_TestCase
{
/**
* @var ArrayDenormalizer
*/
private $denormalizer;
/**
* @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
private $serializer;
protected function setUp()
{
$this->serializer = $this->getMock('Symfony\Component\Serializer\Serializer');
$this->denormalizer = new ArrayDenormalizer();
$this->denormalizer->setSerializer($this->serializer);
}
public function testDenormalize()
{
$this->serializer->expects($this->at(0))
->method('denormalize')
->with(array('foo' => 'one', 'bar' => 'two'))
->will($this->returnValue(new ArrayDummy('one', 'two')));
$this->serializer->expects($this->at(1))
->method('denormalize')
->with(array('foo' => 'three', 'bar' => 'four'))
->will($this->returnValue(new ArrayDummy('three', 'four')));
$result = $this->denormalizer->denormalize(
array(
array('foo' => 'one', 'bar' => 'two'),
array('foo' => 'three', 'bar' => 'four'),
),
__NAMESPACE__.'\ArrayDummy[]'
);
$this->assertEquals(
array(
new ArrayDummy('one', 'two'),
new ArrayDummy('three', 'four'),
),
$result
);
}
public function testSupportsValidArray()
{
$this->serializer->expects($this->once())
->method('supportsDenormalization')
->with($this->anything(), __NAMESPACE__.'\ArrayDummy', $this->anything())
->will($this->returnValue(true));
$this->assertTrue(
$this->denormalizer->supportsDenormalization(
array(
array('foo' => 'one', 'bar' => 'two'),
array('foo' => 'three', 'bar' => 'four'),
),
__NAMESPACE__.'\ArrayDummy[]'
)
);
}
public function testSupportsInvalidArray()
{
$this->serializer->expects($this->any())
->method('supportsDenormalization')
->will($this->returnValue(false));
$this->assertFalse(
$this->denormalizer->supportsDenormalization(
array(
array('foo' => 'one', 'bar' => 'two'),
array('foo' => 'three', 'bar' => 'four'),
),
__NAMESPACE__.'\InvalidClass[]'
)
);
}
public function testSupportsNoArray()
{
$this->assertFalse(
$this->denormalizer->supportsDenormalization(
array('foo' => 'one', 'bar' => 'two'),
__NAMESPACE__.'\ArrayDummy'
)
);
}
}
class ArrayDummy
{
public $foo;
public $bar;
public function __construct($foo, $bar)
{
$this->foo = $foo;
$this->bar = $bar;
}
}

View File

@ -11,6 +11,9 @@
namespace Symfony\Component\Serializer\Tests;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
@ -220,6 +223,51 @@ class SerializerTest extends \PHPUnit_Framework_TestCase
$result = $this->serializer->decode(json_encode($data), 'json');
$this->assertEquals($data, $result);
}
public function testSupportsArrayDeserialization()
{
$serializer = new Serializer(
array(
new GetSetMethodNormalizer(),
new PropertyNormalizer(),
new ObjectNormalizer(),
new CustomNormalizer(),
new ArrayDenormalizer(),
),
array(
'json' => new JsonEncoder(),
)
);
$this->assertTrue(
$serializer->supportsDenormalization(array(), __NAMESPACE__.'\Model[]', 'json')
);
}
public function testDeserializeArray()
{
$jsonData = '[{"title":"foo","numbers":[5,3]},{"title":"bar","numbers":[2,8]}]';
$expectedData = array(
Model::fromArray(array('title' => 'foo', 'numbers' => array(5, 3))),
Model::fromArray(array('title' => 'bar', 'numbers' => array(2, 8))),
);
$serializer = new Serializer(
array(
new GetSetMethodNormalizer(),
new ArrayDenormalizer(),
),
array(
'json' => new JsonEncoder(),
)
);
$this->assertEquals(
$expectedData,
$serializer->deserialize($jsonData, __NAMESPACE__.'\Model[]', 'json')
);
}
}
class Model