Skip to content

Commit 0573f28

Browse files
committed
Support for array denormalization.
1 parent a57ce90 commit 0573f28

File tree

7 files changed

+268
-2
lines changed

7 files changed

+268
-2
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Exception;
13+
14+
class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface
15+
{
16+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
namespace Symfony\Component\Serializer\Normalizer;
4+
5+
/*
6+
* This file is part of the Symfony package.
7+
*
8+
* (c) Fabien Potencier <fabien@symfony.com>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
use Symfony\Component\Serializer\Exception\BadMethodCallException;
15+
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
16+
use Symfony\Component\Serializer\SerializerAwareInterface;
17+
use Symfony\Component\Serializer\SerializerInterface;
18+
19+
/**
20+
* Denormalizes arrays of objects.
21+
*
22+
* @author Alexander M. Turek <me@derrabus.de>
23+
*/
24+
class ArrayDenormalizer implements DenormalizerInterface, SerializerAwareInterface
25+
{
26+
/**
27+
* @var SerializerInterface|DenormalizerInterface
28+
*/
29+
private $serializer;
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
public function denormalize($data, $class, $format = null, array $context = array())
35+
{
36+
if ($this->serializer === null) {
37+
throw new BadMethodCallException('Please set a serializer before calling denormalize()!');
38+
}
39+
if (!is_array($data)) {
40+
throw new InvalidArgumentException('Data expected to be an array, '.gettype($data).' given.');
41+
}
42+
if (substr($class, -2) !== '[]') {
43+
throw new InvalidArgumentException('Unsupported class: '.$class);
44+
}
45+
46+
$serializer = $this->serializer;
47+
$class = substr($class, 0, -2);
48+
49+
return array_map(
50+
function ($data) use ($serializer, $class, $format, $context) {
51+
return $serializer->denormalize($data, $class, $format, $context);
52+
},
53+
$data
54+
);
55+
}
56+
57+
/**
58+
* {@inheritdoc}
59+
*/
60+
public function supportsDenormalization($data, $type, $format = null)
61+
{
62+
return substr($type, -2) === '[]'
63+
&& $this->serializer->supportsDenormalization($data, substr($type, 0, -2), $format);
64+
}
65+
66+
/**
67+
* {@inheritdoc}
68+
*/
69+
public function setSerializer(SerializerInterface $serializer)
70+
{
71+
if (!$serializer instanceof DenormalizerInterface) {
72+
throw new InvalidArgumentException('Expected a serializer that also implements DenormalizerInterface.');
73+
}
74+
75+
$this->serializer = $serializer;
76+
}
77+
}

src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ public function supportsNormalization($data, $format = null)
5959
*/
6060
public function supportsDenormalization($data, $type, $format = null)
6161
{
62+
if (!class_exists($type)) {
63+
return false;
64+
}
65+
6266
$class = new \ReflectionClass($type);
6367

6468
return $class->isSubclassOf('Symfony\Component\Serializer\Normalizer\DenormalizableInterface');

src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public function supportsNormalization($data, $format = null)
135135
*/
136136
public function supportsDenormalization($data, $type, $format = null)
137137
{
138-
return $this->supports($type);
138+
return class_exists($type) && $this->supports($type);
139139
}
140140

141141
/**

src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public function supportsNormalization($data, $format = null)
135135
*/
136136
public function supportsDenormalization($data, $type, $format = null)
137137
{
138-
return $this->supports($type);
138+
return class_exists($type) && $this->supports($type);
139139
}
140140

141141
/**
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Tests\Normalizer;
13+
14+
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
15+
use Symfony\Component\Serializer\SerializerInterface;
16+
17+
class ArrayDenormalizerTest extends \PHPUnit_Framework_TestCase
18+
{
19+
/**
20+
* @var ArrayDenormalizer
21+
*/
22+
private $denormalizer;
23+
24+
/**
25+
* @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject
26+
*/
27+
private $serializer;
28+
29+
protected function setUp()
30+
{
31+
$this->serializer = $this->getMock('Symfony\Component\Serializer\Serializer');
32+
$this->denormalizer = new ArrayDenormalizer();
33+
$this->denormalizer->setSerializer($this->serializer);
34+
}
35+
36+
public function testDenormalize()
37+
{
38+
$this->serializer->expects($this->at(0))
39+
->method('denormalize')
40+
->with(array('foo' => 'one', 'bar' => 'two'))
41+
->will($this->returnValue(new ArrayDummy('one', 'two')));
42+
43+
$this->serializer->expects($this->at(1))
44+
->method('denormalize')
45+
->with(array('foo' => 'three', 'bar' => 'four'))
46+
->will($this->returnValue(new ArrayDummy('three', 'four')));
47+
48+
$result = $this->denormalizer->denormalize(
49+
array(
50+
array('foo' => 'one', 'bar' => 'two'),
51+
array('foo' => 'three', 'bar' => 'four'),
52+
),
53+
__NAMESPACE__.'\ArrayDummy[]'
54+
);
55+
56+
$this->assertEquals(
57+
array(
58+
new ArrayDummy('one', 'two'),
59+
new ArrayDummy('three', 'four'),
60+
),
61+
$result
62+
);
63+
}
64+
65+
public function testSupportsValidArray()
66+
{
67+
$this->serializer->expects($this->once())
68+
->method('supportsDenormalization')
69+
->with($this->anything(), __NAMESPACE__.'\ArrayDummy', $this->anything())
70+
->will($this->returnValue(true));
71+
72+
$this->assertTrue(
73+
$this->denormalizer->supportsDenormalization(
74+
array(
75+
array('foo' => 'one', 'bar' => 'two'),
76+
array('foo' => 'three', 'bar' => 'four'),
77+
),
78+
__NAMESPACE__.'\ArrayDummy[]'
79+
)
80+
);
81+
}
82+
83+
public function testSupportsInvalidArray()
84+
{
85+
$this->serializer->expects($this->any())
86+
->method('supportsDenormalization')
87+
->will($this->returnValue(false));
88+
89+
$this->assertFalse(
90+
$this->denormalizer->supportsDenormalization(
91+
array(
92+
array('foo' => 'one', 'bar' => 'two'),
93+
array('foo' => 'three', 'bar' => 'four'),
94+
),
95+
__NAMESPACE__.'\InvalidClass[]'
96+
)
97+
);
98+
}
99+
100+
public function testSupportsNoArray()
101+
{
102+
$this->assertFalse(
103+
$this->denormalizer->supportsDenormalization(
104+
array('foo' => 'one', 'bar' => 'two'),
105+
__NAMESPACE__.'\ArrayDummy'
106+
)
107+
);
108+
}
109+
}
110+
111+
class ArrayDummy
112+
{
113+
public $foo;
114+
public $bar;
115+
116+
public function __construct($foo, $bar)
117+
{
118+
$this->foo = $foo;
119+
$this->bar = $bar;
120+
}
121+
}

src/Symfony/Component/Serializer/Tests/SerializerTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
namespace Symfony\Component\Serializer\Tests;
1313

14+
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
15+
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
16+
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
1417
use Symfony\Component\Serializer\Serializer;
1518
use Symfony\Component\Serializer\Encoder\JsonEncoder;
1619
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
@@ -220,6 +223,51 @@ public function testDecode()
220223
$result = $this->serializer->decode(json_encode($data), 'json');
221224
$this->assertEquals($data, $result);
222225
}
226+
227+
public function testSupportsArrayDeserialization()
228+
{
229+
$serializer = new Serializer(
230+
array(
231+
new GetSetMethodNormalizer(),
232+
new PropertyNormalizer(),
233+
new ObjectNormalizer(),
234+
new CustomNormalizer(),
235+
new ArrayDenormalizer(),
236+
),
237+
array(
238+
'json' => new JsonEncoder(),
239+
)
240+
);
241+
242+
$this->assertTrue(
243+
$serializer->supportsDenormalization(array(), __NAMESPACE__.'\Model[]', 'json')
244+
);
245+
}
246+
247+
public function testDeserializeArray()
248+
{
249+
$jsonData = '[{"title":"foo","numbers":[5,3]},{"title":"bar","numbers":[2,8]}]';
250+
251+
$expectedData = array(
252+
Model::fromArray(array('title' => 'foo', 'numbers' => array(5, 3))),
253+
Model::fromArray(array('title' => 'bar', 'numbers' => array(2, 8))),
254+
);
255+
256+
$serializer = new Serializer(
257+
array(
258+
new GetSetMethodNormalizer(),
259+
new ArrayDenormalizer(),
260+
),
261+
array(
262+
'json' => new JsonEncoder(),
263+
)
264+
);
265+
266+
$this->assertEquals(
267+
$expectedData,
268+
$serializer->deserialize($jsonData, __NAMESPACE__.'\Model[]', 'json')
269+
);
270+
}
223271
}
224272

225273
class Model

0 commit comments

Comments
 (0)