JMS serializer - Why are new objects not being instantiated through constructor
Asked Answered
H

2

8

Why are new entities instantiated with null for all values except the data in the json, why is the entity constructor not setting defaults - putting a die() in the constructor never gets executed.

Update:

Ok so digging into the code, when no managed entity is found, JMSS will use the doctrine instantiator class to create the entity - its sole job, to create entities without calling the constructor. Is there a reason for this? this is inside JMS\Serializer\Construction\UnserializeObjectConstructor


I've configured the object constructor to use the doctrine object constructor written by JMS, but the same issue happens with and without this.

jms_serializer.object_constructor:
    alias: jms_serializer.doctrine_object_constructor
    public: false

Existing entities are updated without trouble, however new entities are missing all constructor set defaults.

Under 'fields' element 0 is existing, element 1 is new.

array (size=3)
  'id' => int 2
  'name' => string 'Categories' (length=10)
  'fields' => 
    array (size=2)
      0 => 
        array (size=7)
          'id' => int 49
          'displayName' => string 'Car Branded' (length=11)
          'type' => string 'checkboxlist' (length=12)
          'required' => boolean false
          'disabled' => boolean false
          'name' => string 'h49' (length=3)
      1 => 
        array (size=3)
          'type' => string 'email' (length=5)
          'name' => string 'field3491' (length=9)
          'displayName' => string 'Email' (length=5)

The entity looks like this after deserializing:

object(stdClass)[2000]
  public '__CLASS__' => string 'AppBundle\Entity\FormElement' (length=28)
  public 'id' => null
  public 'label' => string 'Email' (length=5)
  public 'type' => string 'email' (length=5)
  public 'defaultValue' => null
  public 'required' => null
  public 'mappedField' => null
  public 'garbageCollection' => null
  public 'sortOrder' => null
  public 'disabled' => null
  public 'uuid' => null
  public 'form' => null
  public 'multiOptions' => null
  public 'filters' => null
  public 'submissions' => null

The entity constructor:

public function __construct()
{
    $this->required = false;
    $this->disabled = false;
    $this->garbageCollection = false;
    $this->sortOrder = 0;
    $this->type = 'text';
}

And finally this is how im deserializing:

$serializer = $this->get('jms_serializer');

$entryForm = $serializer->deserialize($json_data, 'AppBundle\Entity\EntryForm', 'json');
Hophead answered 11/8, 2015 at 17:21 Comment(0)
C
10

The issue is the default ObjectConstructor uses Doctrine's Instantiator, which does not call the class' constructor. To solve this, you can create your own ObjectConstructor that just returns a new instance of the class.

Example:

<?php

namespace AppBundle\Serializer;

use JMS\Serializer\Construction\ObjectConstructorInterface;
use JMS\Serializer\DeserializationContext;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\VisitorInterface;

class ObjectConstructor implements ObjectConstructorInterface
{
    /**
     * {@inheritdoc}
     */
    public function construct(
        VisitorInterface $visitor,
        ClassMetadata $metadata,
        $data,
        array $type,
        DeserializationContext $context
    ) {
        $className = $metadata->name;

        return new $className();
    }
}

If you're using the bundle, just set jms_serializer.unserialize_object_constructor.class parameter to that new class. Otherwise in your builder, use the class as your object constructor.

Chemisorb answered 20/10, 2015 at 12:55 Comment(4)
Can you explain how you set the jms_serializer.unserialize_object_constructor.class to the new class?Glennglenna
He means setting the jms_serializer.unserialize_object_constructor.class configuration option to the path of your own implementation of the ObjectConstructorInterface, which would be AppBundle\Serializer\ObjectConstructor in this example.Watermelon
For serializer-bundle ^3.0 overriding the class parameter no longer works for me. What works is to override the jms_serializer.unserialize_object_constructor service pointing it to class: AppBundle\Serializer\ObjectConstructor.Attachment
Update for people who will run into this issue: this is enough jms_serializer.unserialize_object_constructor: class: AppBundle\Serializer\UnserializeObjectConstructor No need to add aliasing : jms_serializer.object_constructor: alias: jms_serializer.doctrine_object_constructor public: false or you will encounter circular referenceNorman
F
0

What worked for me was simply adding this to the jms_serializer config:

jms_serializer:
    object_constructors:
        doctrine:
            fallback_strategy: "fallback"
Flowerless answered 17/9, 2020 at 7:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.