Skip to content

Commit b3826fb

Browse files
committed
[Serializer] Add the possibility to filter attributes
1 parent 7c5dcfc commit b3826fb

File tree

3 files changed

+123
-5
lines changed

3 files changed

+123
-5
lines changed

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

+33-3
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,20 @@ protected function getAllowedAttributes($classOrObject, array $context, $attribu
236236
*/
237237
protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = array())
238238
{
239-
return !in_array($attribute, $this->ignoredAttributes);
239+
if (in_array($attribute, $this->ignoredAttributes)) {
240+
return false;
241+
}
242+
243+
if (isset($context['attributes'][$attribute])) {
244+
// Nested attributes
245+
return true;
246+
}
247+
248+
if (isset($context['attributes']) && is_array($context['attributes'])) {
249+
return in_array($attribute, $context['attributes'], true);
250+
}
251+
252+
return true;
240253
}
241254

242255
/**
@@ -324,7 +337,7 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
324337
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName) : $paramName;
325338

326339
$allowed = $allowedAttributes === false || in_array($paramName, $allowedAttributes);
327-
$ignored = in_array($paramName, $this->ignoredAttributes);
340+
$ignored = !$this->isAllowedAttribute($class, $paramName, $format, $context);
328341
if (method_exists($constructorParameter, 'isVariadic') && $constructorParameter->isVariadic()) {
329342
if ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) {
330343
if (!is_array($data[$paramName])) {
@@ -341,7 +354,7 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
341354
throw new LogicException(sprintf('Cannot create an instance of %s from serialized data because the serializer inject in "%s" is not a denormalizer', $constructorParameter->getClass(), static::class));
342355
}
343356
$parameterClass = $constructorParameter->getClass()->getName();
344-
$parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $context);
357+
$parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $paramName));
345358
}
346359
} catch (\ReflectionException $e) {
347360
throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $key), 0, $e);
@@ -372,4 +385,21 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
372385

373386
return new $class();
374387
}
388+
389+
/**
390+
* @param array $parentContext
391+
* @param string $attribute
392+
*
393+
* @return array
394+
*
395+
* @internal
396+
*/
397+
protected function createChildContext(array $parentContext, $attribute)
398+
{
399+
if (isset($parentContext['attributes'][$attribute])) {
400+
$parentContext['attributes'] = $parentContext['attributes'][$attribute];
401+
}
402+
403+
return $parentContext;
404+
}
375405
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public function normalize($object, $format = null, array $context = array())
9595
throw new LogicException(sprintf('Cannot normalize attribute "%s" because the injected serializer is not a normalizer', $attribute));
9696
}
9797

98-
$data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $context));
98+
$data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute)));
9999
}
100100

101101
return $data;
@@ -268,7 +268,7 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma
268268
}
269269

270270
if ($this->serializer->supportsDenormalization($data, $class, $format)) {
271-
return $this->serializer->denormalize($data, $class, $format, $context);
271+
return $this->serializer->denormalize($data, $class, $format, $this->createChildContext($context, $attribute));
272272
}
273273
}
274274

src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php

+88
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,70 @@ public function testExtractAttributesRespectsContext()
643643

644644
$this->assertSame(array('foo' => 'bar', 'bar' => 'foo'), $normalizer->normalize($data, null, array('include_foo_and_bar' => true)));
645645
}
646+
647+
public function testAttributesContextNormalize()
648+
{
649+
$normalizer = new ObjectNormalizer();
650+
$serializer = new Serializer(array($normalizer));
651+
652+
$objectInner = new ObjectInner();
653+
$objectInner->foo = 'innerFoo';
654+
$objectInner->bar = 'innerBar';
655+
656+
$objectDummy = new ObjectDummy();
657+
$objectDummy->setFoo('foo');
658+
$objectDummy->setBaz(true);
659+
$objectDummy->setObject($objectInner);
660+
661+
$context = array('attributes' => array('foo', 'baz', 'object' => array('foo')));
662+
$this->assertEquals(
663+
array(
664+
'foo' => 'foo',
665+
'baz' => true,
666+
'object' => array('foo' => 'innerFoo'),
667+
),
668+
$serializer->normalize($objectDummy, null, $context)
669+
);
670+
}
671+
672+
public function testAttributesContextDenormalize()
673+
{
674+
$normalizer = new ObjectNormalizer(null, null, null, new ReflectionExtractor());
675+
$serializer = new Serializer(array($normalizer));
676+
677+
$objectInner = new ObjectInner();
678+
$objectInner->foo = 'innerFoo';
679+
680+
$objectOuter = new ObjectOuter();
681+
$objectOuter->bar = 'bar';
682+
$objectOuter->setInner($objectInner);
683+
684+
$context = array('attributes' => array('bar', 'inner' => array('foo')));
685+
$this->assertEquals($objectOuter, $serializer->denormalize(
686+
array(
687+
'foo' => 'foo',
688+
'bar' => 'bar',
689+
'date' => '2017-02-03',
690+
'inner' => array('foo' => 'innerFoo', 'bar' => 'innerBar'),
691+
), ObjectOuter::class, null, $context));
692+
}
693+
694+
public function testAttributesContextDenormalizeConstructor()
695+
{
696+
$normalizer = new ObjectNormalizer(null, null, null, new ReflectionExtractor());
697+
$serializer = new Serializer(array($normalizer));
698+
699+
$objectInner = new ObjectInner();
700+
$objectInner->bar = 'bar';
701+
702+
$obj = new DummyWithConstructorObjectAndDefaultValue('a', $objectInner);
703+
704+
$context = array('attributes' => array('inner' => array('bar')));
705+
$this->assertEquals($obj, $serializer->denormalize(array(
706+
'foo' => 'b',
707+
'inner' => array('foo' => 'foo', 'bar' => 'bar'),
708+
), DummyWithConstructorObjectAndDefaultValue::class, null, $context));
709+
}
646710
}
647711

648712
class ObjectDummy
@@ -813,6 +877,8 @@ public function setFoo(array $f)
813877

814878
class ObjectOuter
815879
{
880+
public $foo;
881+
public $bar;
816882
private $inner;
817883
private $date;
818884

@@ -910,3 +976,25 @@ class JsonNumber
910976
*/
911977
public $number;
912978
}
979+
980+
class DummyWithConstructorObjectAndDefaultValue
981+
{
982+
private $foo;
983+
private $inner;
984+
985+
public function __construct($foo = 'a', ObjectInner $inner)
986+
{
987+
$this->foo = $foo;
988+
$this->inner = $inner;
989+
}
990+
991+
public function getFoo()
992+
{
993+
return $this->foo;
994+
}
995+
996+
public function getInner()
997+
{
998+
return $this->inner;
999+
}
1000+
}

0 commit comments

Comments
 (0)