Open enumag opened 6 years ago
Hi, thanks for the request. Currently there is nothing in the core that allows something similar. Maybe a pre/post serialization listener can do the work for you here...
That's quite surprising. This seems like a very basic feature that everybody should need when deserializing to a class.
How do I get a list of fields that deserializer is able to fill in the handler?
PropertyMetadata
gives you the type of data you are going to unserialize. from it you can infer what you need
And how do I create a handler that is used always? When I remove the type
key from getSubscribingMethods
I get Each method returned from getSubscribingMethods of service "MyHandler" must have a "type", and "format" attribute.
:( handlers are always per "type"...
Then it's kind of unsolveable with a handler... any tip where in the code of Serializer this should be handled + how the user should configure that they want to use this behavior? I want to send a PR but don't know where to begin.
A nice thing to do can be to allow listeners to be triggered on any type... and that should be something here
According to documentation event subscribers can work on any type already.
what about your message from one hour ago?
And how do I create a handler that is used always? When I remove the type key from getSubscribingMethods I get Each method returned from getSubscribingMethods of service "MyHandler" must have a "type", and "format" attribute.
The link you sent is not about handlers but about Events, right? In event subscribers class
is optional. Should I use events instead of handler then?
My bad, on my first message I wanted to write :
Hi, thanks for the request. Currently there is nothing in the core that allows something similar. Maybe a pre/post serialization listener can do the work for you here...
I've edited the comment for clarity. So you have to implement and event listener.
Ok, I'll try it. Thanks.
I think it will work... This is what I have so far. What do you think?
<?php declare(strict_types = 1);
namespace App\Web;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\PreDeserializeEvent;
class MyEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
[
'event' => 'serializer.pre_deserialize',
'method' => 'onPreDeserialize',
],
];
}
public function onPreDeserialize(PreDeserializeEvent $event)
{
$metadata = $event->getContext()->getMetadataFactory()->getMetadataForClass($event->getType()['name']);
$attributes = array_keys($event->getData());
$properties = array_keys($metadata->propertyMetadata);
$extraAttributes = array_diff($attributes, $properties);
if ($extraAttributes) {
throw new \Exception();
}
}
}
array_keys($event->getData());
will work only for json/yml but the general idea is correct
Ran into this today. I expected invalid properties to produce an error when deserialized. I see from this issue that's not the expected behavior but I agree with the OP that it isn't unreasonable to expect. Perhaps a configuration setting to enable this like Symfony's serializer does?
See https://symfony.com/doc/current/components/serializer.html#deserializing-an-object.
I ran into this issue today as well, I agree that allow_extra_attributes option in the context would be awesome.
The same problem. At the development stage, I would like to receive exceptions in case of differences in the classes and structure of the json.
@enumag your solution does not work if @Discriminator
is used.
https://jmsyst.com/libs/serializer/master/reference/annotations#discriminator
Does anyone have a workaround?
Workaround for @Discriminator
<?php declare(strict_types = 1);
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\PreDeserializeEvent;
class JmsSerializerEventSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents(): array {
return [
[
'event' => 'serializer.pre_deserialize',
'method' => 'onPreDeserialize',
],
];
}
public function onPreDeserialize(PreDeserializeEvent $event) {
$metadata = $event->getContext()->getMetadataFactory()->getMetadataForClass($event->getType()['name']);
if($metadata->discriminatorFieldName) {
$event->getData()[$metadata->discriminatorFieldName];
$class = $metadata->discriminatorMap[$event->getData()[$metadata->discriminatorFieldName]];
$metadata = $event->getContext()->getMetadataFactory()->getMetadataForClass($class);
}
$attributes = array_keys($event->getData());
$properties = array_keys($metadata->propertyMetadata);
$extraAttributes = array_diff($attributes, $properties);
if($extraAttributes) {
throw new \Exception(json_encode($extraAttributes) );
}
}
}
Usage:
/**
* @param string $data
*
* @return Response
*/
protected function deserialize(string $data): Response {
AnnotationRegistry::registerLoader('class_exists');
// $serializer = SerializerBuilder::create()->build();
$builder = SerializerBuilder::create();
$builder->setPropertyNamingStrategy(new IdenticalPropertyNamingStrategy());
$builder->configureListeners(function(EventDispatcherInterface $EventDispatcher) {
$EventDispatcher->addSubscriber(new JmsSerializerEventSubscriber() );
});
$serializer = $builder->build();
$object = $serializer->deserialize($data, Response::class, 'json');
return $object;
}
I need to deserialize a json into a class. It works fine but I noticed that when I add a field to the json that does not exist on the class it's silently ignored. I need to know about it somehow to throw a correct exception - such data are considered as invalid for my use case.