Fixing the JMS Serializer inheritance problem
Using the JMS serializer in recent project, I ran into the problem that the serializer does not support inheritance in a handler configuration. A handler is used to to change the serialization, or deserialization process for a single type/format combination.
In my specific use case I was looking for a simple way to return the string representation of my value objects instead of returning the value objects itself. In my ideal scenario a code snippet like this should have been sufficient:
$seralizer = \JMS\Serializer\SerializerBuilder::create();
$seralizer->configureHandlers(
function (\JMS\Serializer\Handler\HandlerRegistry $registry) {
$registry->registerHandler(
'serialization',
ValueObject::class,
'json',
function ($visitor, ValueObject $obj, array $type) {
return $obj->__toString();
}
);
}
);
But since the ValueObject is a super-type, the JMS serializer ignores the handler and renders all sub-types as is. To solve the problem quickly - never a good idea, I know - I simply copy-and-pasted the code above for all my different value objects. That worked fine, but was way too much configuration code for my taste.
While I was checking the JMS documentation to find a better solution, I realized that the pre_serialize event listener is able to modify the type that is being used in the serialization process. And as it turns out the "fix" was trivial:
In the pre_serialize event I simply check if type of the object that should be serialized is ValueObject. If that is the case, the event type gets set to ValueObject. The handler defined above will use that event type to look up which custom serialize function should be used.
$seralizer->configureListeners(function(EventDispatcher $dispatcher) {
$dispatcher->addListener('serializer.pre_serialize',
function(PreSerializeEvent $event) {
$object = $event->getObject();
if ($object instanceof ValueObject) {
$event->setType(ValueObject::class);
}
}
);
});