Skip to content

Commit

Permalink
Support for array denormalization.
Browse files Browse the repository at this point in the history
  • Loading branch information
derrabus committed May 26, 2015
1 parent a57ce90 commit 0573f28
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 2 deletions.
@@ -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
{
}
77 changes: 77 additions & 0 deletions src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php
@@ -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;
}
}
Expand Up @@ -59,6 +59,10 @@ public function supportsNormalization($data, $format = null)
*/
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');
Expand Down
Expand Up @@ -135,7 +135,7 @@ public function supportsNormalization($data, $format = null)
*/
public function supportsDenormalization($data, $type, $format = null)
{
return $this->supports($type);
return class_exists($type) && $this->supports($type);
}

/**
Expand Down
Expand Up @@ -135,7 +135,7 @@ public function supportsNormalization($data, $format = null)
*/
public function supportsDenormalization($data, $type, $format = null)
{
return $this->supports($type);
return class_exists($type) && $this->supports($type);
}

/**
Expand Down
@@ -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;
}
}
48 changes: 48 additions & 0 deletions src/Symfony/Component/Serializer/Tests/SerializerTest.php
Expand Up @@ -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;
Expand Down Expand Up @@ -220,6 +223,51 @@ public function testDecode()
$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
Expand Down

0 comments on commit 0573f28

Please sign in to comment.