Inari
Inari

Reputation: 31

Symfony+JMS Serializer deserialize into existing object

I've been wondering if it's possible to use the JMS Serializer to deserialize JSON into an existing object.

Usually that would be useful for updating an existing object with new data that you have in a JSON format. Symfony's standard deserializer seems to offer that, but I can't seem to find anything about this with JMS. Have to use JMS though if I want the serializedName Annotation option.

The "workaround" is to deserialize and then use Doctrine's EntityManager to merge, but that only works so well, and you can't easily discern which fields are updated if the JSON doesn't contain every single field.

Upvotes: 2

Views: 3680

Answers (2)

yakob abada
yakob abada

Reputation: 1287

I have struggled to find the solution but finally found it and here we go:

  • your services.yaml
    jms_serializer.object_constructor:
        alias: jms_serializer.initialized_object_constructor

    jms_serializer.initialized_object_constructor:
        class: App\Service\InitializedObjectConstructor
        arguments: ["@jms_serializer.unserialize_object_constructor"]
  • create class App\Service\InitializedObjectConstructor.php
<?php

declare(strict_types=1);

namespace App\Service;

use JMS\Serializer\Construction\ObjectConstructorInterface;
use JMS\Serializer\DeserializationContext;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Visitor\DeserializationVisitorInterface;

class InitializedObjectConstructor implements ObjectConstructorInterface
{
    private $fallbackConstructor;

    /**
     * @param ObjectConstructorInterface $fallbackConstructor Fallback object constructor
     */
    public function __construct(ObjectConstructorInterface $fallbackConstructor)
    {
        $this->fallbackConstructor = $fallbackConstructor;
    }

    /**
     * {@inheritdoc}
     */
    public function construct(
        DeserializationVisitorInterface $visitor,
        ClassMetadata $metadata,
        $data,
        array $type,
        DeserializationContext $context
    ): ?object {
        if ($context->hasAttribute('target') && 1 === $context->getDepth()) {
            return $context->getAttribute('target');
        }

        return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
    }
}

  • in your controller or your service file
        $object = $this->entityManager->find('YourEntityName', $id);

        $context = new DeserializationContext();
        $context->setAttribute('target', $object);

        $data = $this->serializer->deserialize($request->getContent(), 'YourEntityClassName', 'json', $context);

Upvotes: 4

Inari
Inari

Reputation: 31

So this can be done, I haven't fully worked out how, but I switched away from JMS again, so just for reference, since I guess it's better than keeping the question open for no reason:

https://github.com/schmittjoh/serializer/issues/79 and you might find more digging around the GitHub too.

Upvotes: 1

Related Questions