Description
Q | A |
---|---|
Bug report? | yes? |
Feature request? | yes? |
BC Break report? | no |
RFC? | no |
Symfony version | tbd |
My colleagues and I have come across the following problem quite often:
/*
* This works like expected \o/
*/
$this->get('serializer')->deserialize('{"updatedAt": "2017-06-15"}', User::class, 'json');
/*
* Get a weird exception:
* Expected argument of type "DateTime", "string" given
*/
$this->get('serializer')->deserialize('{"updated_at": "2017-06-15"}', User::class, 'json');
This exception is really weird because it says it can find the setter but not a type. It seems to be a PropertyInfo or Normalizer problem. So after getting this exception you naturally check the framework configuration, or if there is a missing normalizer or a missing property info etc. But the real cause is the PropertyAccessor, because it always camelizes the property name.
This is what happens in the background:
- PropertyInfo doesn't camelize the key 'updated_at', so it can't find the property 'updatedAt' or setter 'setUpdatedAt'.
- Because of that it doesn't get the correct type to convert the string to a DateTime object.
- PropertyAccess always camelizes the key and it tries to set the string value. But it doesn't work because of type mismatch and you get an invalid argument exception.
I have created a new Symfony Standard Edition project with minimal changes to reproduce this issue: https://github.com/DavidBadura/symfony-serializer
So what can we do here?
- add a new PropertyAccess option
PropertyAccess gets a flag "don't camelize" (tbd), so you can create a special PropertyAccessor for the Serializer component. So the PropertyAccessor can throw an property or setter not found
exception.
private function camelize($string)
{
if ($this->doNotCamelize) { // tbd
return ucfirst($string);
}
return str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
}
- ObjectNormalizer checks property name
ObjectNormalizer checks if the property with this name exists otherwise it throws an exception. But
it won't be as performant as before and i think it shouldn't be its scope.
The goal should be to throw a DX friendly exception. I can create a merge request if we decide what we want to do :)