From 8f13ff51097713fd991222cf53a389e5a57356f6 Mon Sep 17 00:00:00 2001 From: katinsv Date: Mon, 21 Jun 2021 11:56:53 +0300 Subject: [PATCH] Context::getCurrentPath() returns list index if current element is part of the list --- src/Context.php | 3 +++ src/GraphNavigator/SerializationGraphNavigator.php | 13 +++++++++++++ src/JsonSerializationVisitor.php | 1 + src/Metadata/ClassMetadata.php | 7 +++++++ tests/Serializer/ContextTest.php | 7 ++++++- 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Context.php b/src/Context.php index 36adc2e69..eb6fc6182 100644 --- a/src/Context.php +++ b/src/Context.php @@ -253,6 +253,9 @@ public function getCurrentPath(): array $paths = []; foreach ($this->metadataStack as $metadata) { + if ($metadata instanceof ClassMetadata && $metadata->positionInList !== null) { + array_unshift($paths, $metadata->positionInList); + } if ($metadata instanceof PropertyMetadata) { array_unshift($paths, $metadata->name); } diff --git a/src/GraphNavigator/SerializationGraphNavigator.php b/src/GraphNavigator/SerializationGraphNavigator.php index 5118b1571..dbe344663 100644 --- a/src/GraphNavigator/SerializationGraphNavigator.php +++ b/src/GraphNavigator/SerializationGraphNavigator.php @@ -116,6 +116,14 @@ public function initialize(VisitorInterface $visitor, Context $context): void */ public function accept($data, ?array $type = null) { + // Element could be one of the element list + $positionInList = null; + if (isset($type['position_in_list'])) { + $positionInList = $type['position_in_list']; + unset($type['position_in_list']); + $type = $type ?: null; + } + // If the type was not given, we infer the most specific type from the // input data in serialization mode. if (null === $type) { @@ -219,6 +227,11 @@ public function accept($data, ?array $type = null) $metadata = $this->metadataFactory->getMetadataForClass($type['name']); \assert($metadata instanceof ClassMetadata); + if ($positionInList !== null) { + $metadata = clone $metadata; + $metadata->positionInList = $positionInList; + } + if ($metadata->usingExpression && null === $this->expressionExclusionStrategy) { throw new ExpressionLanguageRequiredException(sprintf('To use conditional exclude/expose in %s you must configure the expression language.', $metadata->name)); } diff --git a/src/JsonSerializationVisitor.php b/src/JsonSerializationVisitor.php index fbee58249..5e485654f 100644 --- a/src/JsonSerializationVisitor.php +++ b/src/JsonSerializationVisitor.php @@ -89,6 +89,7 @@ public function visitArray(array $data, array $type) $elType = $this->getElementType($type); foreach ($data as $k => $v) { + $elType['position_in_list'] = $k; try { $v = $this->navigator->accept($v, $elType); } catch (NotAcceptableException $e) { diff --git a/src/Metadata/ClassMetadata.php b/src/Metadata/ClassMetadata.php index 1b6bab2f7..7ff87935e 100644 --- a/src/Metadata/ClassMetadata.php +++ b/src/Metadata/ClassMetadata.php @@ -84,6 +84,13 @@ class ClassMetadata extends MergeableClassMetadata */ public $isMap = false; + /** + * @internal + * + * @var int|null + */ + public $positionInList = null; + /** * @var bool */ diff --git a/tests/Serializer/ContextTest.php b/tests/Serializer/ContextTest.php index ef2f7eb14..31d6cca42 100644 --- a/tests/Serializer/ContextTest.php +++ b/tests/Serializer/ContextTest.php @@ -62,24 +62,29 @@ public function testSerializationContextPathAndDepth() $exclusionStrategy->expects($this->any()) ->method('shouldSkipProperty') ->with($this->anything(), $this->callback(static function (SerializationContext $context) use ($self, $objects) { - $expectedDepth = $expectedPath = null; + $expectedCurrentPath = $expectedDepth = $expectedPath = null; if ($context->getObject() === $objects[0]) { $expectedDepth = 1; $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node'; + $expectedCurrentPath = []; } elseif ($context->getObject() === $objects[1]) { $expectedDepth = 2; $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node'; + $expectedCurrentPath = ['children', 0]; } elseif ($context->getObject() === $objects[2]) { $expectedDepth = 2; $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node'; + $expectedCurrentPath = ['children', 1]; } elseif ($context->getObject() === $objects[3]) { $expectedDepth = 3; $expectedPath = 'JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node -> JMS\Serializer\Tests\Fixtures\Node'; + $expectedCurrentPath = ['children', 1, 'children', 0]; } $self->assertEquals($expectedDepth, $context->getDepth(), 'shouldSkipProperty depth'); $self->assertEquals($expectedPath, $context->getPath(), 'shouldSkipProperty path'); + $self->assertEquals($expectedCurrentPath, $context->getCurrentPath(), 'Unexpected context current path'); return true; }))