Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9b8a7ba

Browse files
mtarldnicolas-grekas
authored andcommitted
[Serializer] Fix denormalization of nested array with key types
1 parent 93aafc2 commit 9b8a7ba

3 files changed

Lines changed: 60 additions & 3 deletions

File tree

Normalizer/AbstractObjectNormalizer.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -534,18 +534,18 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
534534
$builtinType = Type::BUILTIN_TYPE_OBJECT;
535535
$class = $collectionValueType->getClassName().'[]';
536536

537-
if (\count($collectionKeyType = $type->getCollectionKeyTypes()) > 0) {
537+
if ($collectionKeyType = $type->getCollectionKeyTypes()) {
538538
$context['key_type'] = \count($collectionKeyType) > 1 ? $collectionKeyType : $collectionKeyType[0];
539539
}
540540

541541
$context['value_type'] = $collectionValueType;
542-
} elseif ($type->isCollection() && \count($collectionValueType = $type->getCollectionValueTypes()) > 0 && Type::BUILTIN_TYPE_ARRAY === $collectionValueType[0]->getBuiltinType()) {
542+
} elseif ($type->isCollection() && ($collectionValueType = $type->getCollectionValueTypes()) && Type::BUILTIN_TYPE_ARRAY === $collectionValueType[0]->getBuiltinType()) {
543543
// get inner type for any nested array
544544
[$innerType] = $collectionValueType;
545545

546546
// note that it will break for any other builtinType
547547
$dimensions = '[]';
548-
while (\count($innerType->getCollectionValueTypes()) > 0 && Type::BUILTIN_TYPE_ARRAY === $innerType->getBuiltinType()) {
548+
while ($innerType->getCollectionValueTypes() && Type::BUILTIN_TYPE_ARRAY === $innerType->getBuiltinType()) {
549549
$dimensions .= '[]';
550550
[$innerType] = $innerType->getCollectionValueTypes();
551551
}
@@ -554,6 +554,12 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
554554
// the builtinType is the inner one and the class is the class followed by []...[]
555555
$builtinType = $innerType->getBuiltinType();
556556
$class = $innerType->getClassName().$dimensions;
557+
558+
if ($collectionKeyType = $type->getCollectionKeyTypes()) {
559+
$context['key_type'] = \count($collectionKeyType) > 1 ? $collectionKeyType : $collectionKeyType[0];
560+
}
561+
562+
$context['value_type'] = $collectionValueType[0];
557563
} else {
558564
// default fallback (keep it as array)
559565
$builtinType = $type->getBuiltinType();

Normalizer/ArrayDenormalizer.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,17 @@ public function denormalize(mixed $data, string $type, ?string $format = null, a
6262
return $keyType->getBuiltinType();
6363
}, \is_array($keyType = $context['key_type'] ?? []) ? $keyType : [$keyType]);
6464

65+
$valueType = $context['value_type'] ?? null;
66+
if ($valueType instanceof Type && $valueType->isCollection()) {
67+
if ($collectionKeyTypes = $valueType->getCollectionKeyTypes()) {
68+
$context['key_type'] = \count($collectionKeyTypes) > 1 ? $collectionKeyTypes : $collectionKeyTypes[0];
69+
}
70+
71+
if ($collectionValueTypes = $valueType->getCollectionValueTypes()) {
72+
$context['value_type'] = $collectionValueTypes[0];
73+
}
74+
}
75+
6576
foreach ($data as $key => $value) {
6677
$subContext = $context;
6778
$subContext['deserialization_path'] = ($context['deserialization_path'] ?? false) ? \sprintf('%s[%s]', $context['deserialization_path'], $key) : "[$key]";

Tests/DeserializeNestedArrayOfObjectsTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,30 @@ public function testPropertyPhpDocWithKeyTypes()
102102
self::assertArrayHasKey(3, $zoo->animalsGenerics);
103103
self::assertInstanceOf(Animal::class, $zoo->animalsGenerics[3]);
104104
}
105+
106+
public function testNestedArrayWithStringKeyUnderList()
107+
{
108+
$json = <<<EOF
109+
{
110+
"foos": [{"operators": {"something": [{"name": "Bug"}]}}]
111+
}
112+
EOF;
113+
$serializer = new Serializer([
114+
new ObjectNormalizer(null, null, null, new PhpDocExtractor()),
115+
new ArrayDenormalizer(),
116+
], ['json' => new JsonEncoder()]);
117+
118+
/** @var BarWithNestedKeyTypes $bar */
119+
$bar = $serializer->deserialize($json, BarWithNestedKeyTypes::class, 'json');
120+
121+
self::assertCount(1, $bar->foos);
122+
self::assertInstanceOf(FooWithStringKeyedList::class, $bar->foos[0]);
123+
self::assertCount(1, $bar->foos[0]->operators);
124+
self::assertArrayHasKey('something', $bar->foos[0]->operators);
125+
self::assertCount(1, $bar->foos[0]->operators['something']);
126+
self::assertInstanceOf(Animal::class, $bar->foos[0]->operators['something'][0]);
127+
self::assertSame('Bug', $bar->foos[0]->operators['something'][0]->getName());
128+
}
105129
}
106130

107131
class Zoo
@@ -178,3 +202,19 @@ public function setName($name)
178202
$this->name = $name;
179203
}
180204
}
205+
206+
class FooWithStringKeyedList
207+
{
208+
/** @param array<string, list<Animal>> $operators */
209+
public function __construct(public array $operators = [])
210+
{
211+
}
212+
}
213+
214+
class BarWithNestedKeyTypes
215+
{
216+
/** @param list<FooWithStringKeyedList> $foos */
217+
public function __construct(public array $foos = [])
218+
{
219+
}
220+
}

0 commit comments

Comments
 (0)