Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Serializer] Fix deserializing nested arrays of objects with mixed keys #50933

Merged
merged 1 commit into from Jul 27, 2023

Conversation

HypeMC
Copy link
Contributor

@HypeMC HypeMC commented Jul 11, 2023

Q A
Branch? 5.4
Bug fix? yes
New feature? no
Deprecations? no
Tickets Fix #50675
License MIT
Doc PR -

Currently an error is thrown when trying to deserialize the following nested array of objects:

use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;

$serializer = new Serializer([
    new ObjectNormalizer(null, null, null, new PhpDocExtractor()),
    new ArrayDenormalizer(),
], ['json' => new JsonEncoder()]);

class Outer
{
    /**
     * @var array<int|string, Inner>
     */
    public array $inners;
}

class Inner
{
    public string $name;
}

$serializer->deserialize('{"inners": {"1": {"name": "One"}, "two": {"name": "Two"}}}', Outer::class, 'json');
Fatal error: Uncaught Symfony\Component\Serializer\Exception\NotNormalizableValueException:
The type of the key "two" must be "int" ("string" given).

The same happens when using generics, e.g. ArrayCollection<Inner>.

@HypeMC HypeMC force-pushed the fix-union-in-array-keys branch 3 times, most recently from ed46804 to 3ff1be8 Compare July 13, 2023 16:44
Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check the follow patch please?

diff --git a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php
index 05d8b79d1a..dfd023a437 100644
--- a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php
@@ -49,9 +49,7 @@ class ArrayDenormalizer implements ContextAwareDenormalizerInterface, Denormaliz
 
         $type = substr($type, 0, -2);
 
-        $builtinTypes = array_map(static function (Type $keyType) {
-            return $keyType->getBuiltinType();
-        }, \is_array($keyType = $context['key_type'] ?? []) ? $keyType : [$keyType]);
+        $builtinTypes = array_map(static function ($keyType) { return $keyType->getBuiltinType(); }, (array) ($context['key_type'] ?? []));
 
         foreach ($data as $key => $value) {
             $subContext = $context;
@@ -109,20 +107,12 @@ class ArrayDenormalizer implements ContextAwareDenormalizerInterface, Denormaliz
      */
     private function validateKeyType(array $builtinTypes, $key, string $path): void
     {
         if (!$builtinTypes) {
             return;
         }
 
-        $unexpectedDataType = true;
         foreach ($builtinTypes as $builtinType) {
             if (('is_'.$builtinType)($key)) {
-                $unexpectedDataType = false;
-                break;
+                return;
             }
         }
 
-        if ($unexpectedDataType) {
-            throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, implode('", "', $builtinTypes), get_debug_type($key)), $key, $builtinTypes, $path, true);
-        }
+        throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, implode('", "', $builtinTypes), get_debug_type($key)), $key, $builtinTypes, $path, true);
     }
 }
diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json
index 1b9537f8f8..11e67a8a7e 100644
--- a/src/Symfony/Component/Serializer/composer.json
+++ b/src/Symfony/Component/Serializer/composer.json
@@ -34,7 +34,7 @@
         "symfony/http-kernel": "^4.4|^5.0|^6.0",
         "symfony/mime": "^4.4|^5.0|^6.0",
         "symfony/property-access": "^5.4|^6.0",
-        "symfony/property-info": "^5.4.24|^6.0",
+        "symfony/property-info": "^5.4.24|^6.2.11",
         "symfony/uid": "^5.3|^6.0",
         "symfony/validator": "^4.4|^5.0|^6.0",
         "symfony/var-dumper": "^4.4|^5.0|^6.0",
@@ -47,7 +47,7 @@
         "phpdocumentor/type-resolver": "<1.4.0",
         "symfony/dependency-injection": "<4.4",
         "symfony/property-access": "<5.4",
-        "symfony/property-info": "<5.4.24",
+        "symfony/property-info": "<5.4.24|>=6,<6.2.11",
         "symfony/uid": "<5.3",
         "symfony/yaml": "<4.4"
     },

@HypeMC
Copy link
Contributor Author

HypeMC commented Jul 27, 2023

Can you check the follow patch please?

@nicolas-grekas The following part won't work:

-        $builtinTypes = array_map(static function (Type $keyType) {
-            return $keyType->getBuiltinType();
-        }, \is_array($keyType = $context['key_type'] ?? []) ? $keyType : [$keyType]);
+        $builtinTypes = array_map(static function ($keyType) { return $keyType->getBuiltinType(); }, (array) ($context['key_type'] ?? []));

$context['key_type'] is not a scalar but an instance of \Symfony\Component\PropertyInfo\Type.

@nicolas-grekas
Copy link
Member

Thank you @HypeMC.

@nicolas-grekas nicolas-grekas merged commit 8492f10 into symfony:5.4 Jul 27, 2023
10 of 11 checks passed
@HypeMC HypeMC deleted the fix-union-in-array-keys branch July 27, 2023 16:19
This was referenced Jul 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants