-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Description
Symfony version(s) affected: 4.3.3
Description
When an object contains properties of \stdClass
, serialization fails.
[Possible secondary issue: ObjectNormalizer
fails to read data from this object]
How to reproduce
My first attempt with the standard Symfony configuration of the serializer (using ObjectNormalizer
is missing most properties.
Code
<?php
// Setup instructions:
// composer require gocardless/gocardless-pro
// composer require symfony/serializer-pack
use GoCardlessPro\Resources\Event;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
require_once 'vendor/autoload.php';
// This - I hope - mimics the Symfony framework default Serializer configuration
$encoders = [new XmlEncoder(), new JsonEncoder()];
$normalizers = [new ObjectNormalizer()];
$serializer = new Serializer($normalizers, $encoders);
// End configuring the serializer
// Begin configuring the object to serialize
$json = '{"id":"EV0000000000000","created_at":"2019-08-06T20:09:15.731Z","action":"active","resource_type":"mandates","details":{"origin":"gocardless","cause":"mandate_activated","description":"The time window after submission for the banks to refuse a mandate has ended without any errors being received, so this mandate is now active."},"links":{"mandate":"MD00000000000"},"metadata":[],"model_name":"Event"}';
$event = new Event(json_decode($json));
echo "Expected\n";
// Get rid of the line breaks:
echo json_encode(json_decode($json));
echo "\n\nActual\n";
echo $serializer->serialize($event, 'json');
Output
Expected
{"id":"EV0000000000000","created_at":"2019-08-06T20:09:15.731Z","action":"active","resource_type":"mandates","details":{"origin":"gocardless","cause":"mandate_activated","description":"The time window after submission for the banks to refuse a mandate has ended without any errors being received, so this mandate is now active."},"links":{"mandate":"MD00000000000"},"metadata":[],"model_name":"Event"}
Actual
{"api_response":null}
I assume that is expected behaviour (though don't understand why) so tried a different normalizer: PropertyNormalizer
. This worked if the structure was all array
s (see comment in code) but not with objects:
Code
<?php
// Setup instructions:
// composer require gocardless/gocardless-pro
// composer require symfony/serializer-pack
use GoCardlessPro\Resources\Event;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
use Symfony\Component\Serializer\Serializer;
require_once 'vendor/autoload.php';
// Custom Serializer configuration
$encoders = [new XmlEncoder(), new JsonEncoder()];
$normalizers = [new PropertyNormalizer()];
$serializer = new Serializer($normalizers, $encoders);
// End configuring the serializer
$json = '{"id":"EV0000000000000","created_at":"2019-08-06T20:09:15.731Z","action":"active","resource_type":"mandates","details":{"origin":"gocardless","cause":"mandate_activated","description":"The time window after submission for the banks to refuse a mandate has ended without any errors being received, so this mandate is now active."},"links":{"mandate":"MD00000000000"},"metadata":[],"model_name":"Event"}';
// All problems can be fixed if I could change this to $event = new Event(json_decode($json, true));
// But I can't - the library that creates these objects doesn't
// So I need the serializer to handle stdClass objects
$event = new Event(json_decode($json));
echo "Expected\n";
// Get rid of the line breaks:
echo json_encode(json_decode($json));
echo "\n\nActual\n";
// IMO this should work, but it throws an exception:
// PHP Fatal error: Uncaught Symfony\Component\Serializer\Exception\NotNormalizableValueException: Could not normalize object of type stdClass, no supporting normalizer found.
#echo $serializer->serialize($event, 'json');
// So I try doing my own conversions:
// It still throws the same exception
$callback = function ($innerObject, $outerObject, string $attributeName, string $format = null, array $context = []) {
return $innerObject instanceof \stdClass ? (array)$innerObject : $innerObject;
};
echo $serializer->serialize($event, 'json', [
'callbacks' => [
// This assumes I know all the object properties that are stdClass - which I won't
'details' => $callback,
'links' => $callback,
'metadata' => $callback,
'data' => $callback,
]
]);
Output
Expected
{"id":"EV0000000000000","created_at":"2019-08-06T20:09:15.731Z","action":"active","resource_type":"mandates","details":{"origin":"gocardless","cause":"mandate_activated","description":"The time window after submission for the banks to refuse a mandate has ended without any errors being received, so this mandate is now active."},"links":{"mandate":"MD00000000000"},"metadata":[],"model_name":"Event"}
Actual
PHP Fatal error: Uncaught Symfony\Component\Serializer\Exception\NotNormalizableValueException: Could not normalize object of type stdClass, no supporting normalizer found. in Z:\path\to\vendor\symfony\serializer\Serializer.php:173
Stack trace:
#0 Z:\path\to\vendor\symfony\serializer\Serializer.php(162): Symfony\Component\Serializer\Serializer->normalize(Object(stdClass), 'json', Array)
#1 Z:\path\to\vendor\symfony\serializer\Normalizer\AbstractObjectNormalizer.php(206): Symfony\Component\Serializer\Serializer->normalize(Array, 'json', Array)
#2 Z:\path\to\vendor\symfony\serializer\Serializer.php(152): Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer->normalize(Object(GoCardlessPro\Resources\Event), 'json', Array)
#3 Z:\path\to\vendor\symfony\serializer\Serializer.php(125): Symfony\Component\Serializer\Serializer->normalize in Z:\path\to\vendor\symfony\serializer\Serializer.php on line 173
Fatal error: Uncaught Symfony\Component\Serializer\Exception\NotNormalizableValueException: Could not normalize object of type stdClass, no supporting normalizer found. in Z:\path\to\vendor\symfony\serializer\Serializer.php on line 173
Symfony\Component\Serializer\Exception\NotNormalizableValueException: Could not normalize object of type stdClass, no supporting normalizer found. in Z:\path\to\vendor\symfony\serializer\Serializer.php on line 173
Call Stack:
0.0002 405288 1. {main}() Z:\path\to\index.php:0
0.0130 1260760 2. Symfony\Component\Serializer\Serializer->serialize() Z:\path\to\index.php:45
0.0131 1261136 3. Symfony\Component\Serializer\Serializer->normalize() Z:\path\to\vendor\symfony\serializer\Serializer.php:125
0.0131 1262264 4. Symfony\Component\Serializer\Normalizer\PropertyNormalizer->normalize() Z:\path\to\vendor\symfony\serializer\Serializer.php:152
0.0138 1267416 5. Symfony\Component\Serializer\Serializer->normalize() Z:\path\to\vendor\symfony\serializer\Normalizer\AbstractObjectNormalizer.php:206
0.0138 1267792 6. Symfony\Component\Serializer\Serializer->normalize() Z:\path\to\vendor\symfony\serializer\Serializer.php:162
Possible Solution
Write ones own normalizer for stdClass
? As it's a PHP built-in class, I'm not clear this is intended; why can't the standard object normalizers cope with it?
Additional context