JMSSerializerBundle icon indicating copy to clipboard operation
JMSSerializerBundle copied to clipboard

@Discriminator Error: The error is: The discriminator field name "type" for base-class "MyBundle\Entity\MyClass" was not found in input data

Open danieledangeli opened this issue 11 years ago • 74 comments

Hi, i have a problem with @discriminator for abstract class. For example, this structure of classes don't work correctly when I try to serialize and deserialize the objects.

class Saloon
{
    /**
     *@Type("integer")
     */
    public $id;

    /**
     * @Type("MyME\HelloBundle\Controller\Vehicle")
     */
    public $veichle;
}


/**
 * @Discriminator(field = "type", map = {"car": "MyME\HelloBundle\Controller\Car", * "moped":"MyME\HelloBundle\Controller\Moped"})
 */
abstract class Vehicle {

    public $id;


}
class Car extends Vehicle {

    /**
     *@Type("integer")
    */
    public $id;
    /**
     *@Type("string")
     */
    public $car_at;
}
class Moped extends Vehicle {

    /**
     *@Type("integer")
     */
    public $id;
    /**
     *@Type("string")
     */
    public $moped_at;

}

example usage:

        $car = new Car();
        $car->id = 1; 
        $car->car_at = "car";

        $car2 = new Moped();
        $car2->id = 2; 
        $car2->moped_at = "moped";


        $saloon = new Saloon();
        $sal0on->veichle = $car2;
        $serializer = SerializerBuilder::create()->build();
        $jsonveic = $serializer->serialize($saloon,'json');

        $saloon_des = $serializer->deserialize($jsonveic,'MyME\HelloBundle\Controller\Saloon','json');

The error is: The discriminator field name "type" for base-class "MyME\HelloBundle\Controller\Vehicle" was not found in input data

danieledangeli avatar May 03 '13 11:05 danieledangeli

Hi.

I have the same issue. Is it not possible to have the discriminator field automatically created when serializing?

Moreover, it seems like when I serialize, the fields from the extending classes are not exposed.

baptisteCable avatar Sep 18 '13 14:09 baptisteCable

The problem is the abstract class. If you try to serialize only Car or Moped class the discriminator field is written in the serialized object. I remember that I tried to use "PreSerialize" and "PostSerialize" mehtod to avoid this problem, but I didn't remember the solution :S

danieledangeli avatar Sep 18 '13 14:09 danieledangeli

Thanks for your answer.

I doesn't work. I have the following (simplified) classes.

class ExerciseModelResource
{
    /**
     * @Type("string")
     */
    private $title;

    /**
     * @Type("CommonModel")
     */
    private $content;
}

/**
 * Abstract class for the exercise models.
 *
 * @Discriminator(field = "exercise_model_type", map = {
 *    "group-items": "GroupItems,
 *    "multiple-choice": "MultipleChoice"
 * })
 */
abstract class CommonModel
{
    /**
     * @Type("string")
     */
    protected $wording;
}

class GroupItems extends CommonModel
{
    /**
     * @Type("string")
     */
    private $displayGroupNames;
}

class MultipleChoice extends CommonModel
{
    /**
     * @Type("array")
     */
    private $questionBlocks = array();
}

When I serialize an ExerciseModelResource, I want the discriminator field to be written in the serialized object. I cannot put a more specific classe in @Type on $comment because it is not always the same when I serialize.

And in addition, I tried to replace this Type by MultipleChoice, just to see, be it added no field.

How can I do?

EDIT : I had a problem with the serializer I was using. Now, I get the field when I serialize if I specify no type (no abstract class). But as I want to use this class to serialize AND deserialize, I need one thing: how can I ignore an annotation (@Type) when I serialize and take it in account when I deserialize?

baptisteCable avatar Sep 18 '13 15:09 baptisteCable

:+1: This issue has been a problem in my project as well

DavidMikeSimon avatar Feb 12 '14 20:02 DavidMikeSimon

Look at the @Serializer\VirtualProperty Annotation. Worked for me.

class Car extends Vehicle {
    /**
     * @Serializer\VirtualProperty
     */
    public function getType()
    {
        return 'car';
    }
}

class Moped extends Vehicle {
    /**
     * @Serializer\VirtualProperty
     */
    public function getType()
    {
        return 'moped';
    }
}

jmiridis avatar Mar 28 '14 12:03 jmiridis

What about the deserialize method? It works in the serialization as well, but what's happen when you try to deserialize it? The virtual property is not a discrimination field to mapping a "json" or a string representation in the right class! I think. Sorry I'm curios :D

danieledangeli avatar Mar 28 '14 13:03 danieledangeli

My problem was that my discriminator property (discr) was not included during serialization but was required for the deserialization process. I didn't know how to get the Serializer to include it. What I achieved by adding this virtual property was to get the serializer to include it into the serialized format, thus deserialization worked fine after that.

jmiridis avatar Mar 28 '14 14:03 jmiridis

I tried the mentioned work-around but couldn't get this to work

/**
 * @JMS\Discriminator(field = "type", map = {"car": "...\Test\Car", "moped":"...\Test\Moped"})
 */
abstract class Vehicle {

    public $id;
}

class Car extends Vehicle {

    /**
     *@JMS\Type("integer")
    */
    public $id;
    /**
     *@JMS\Type("string")
     */
    public $car_at;

    /**
     * @JMS\VirtualProperty
     */
    public function getType()
    {
        return 'car';
    }
}

class Moped extends Vehicle {

    /**
     *@JMS\Type("integer")
     */
    public $id;
    /**
     *@JMS\Type("string")
     */
    public $moped_at;

    /**
     * @JMS\VirtualProperty
     */
    public function getType()
    {
        return 'moped';
    }

}

class Saloon
{
    /**
     *@JMS\Type("integer")
     */
    public $id;

    /**
     * @JMS\Type("...\Test\Vehicle")
     */
    public $veichle;
}

    //In controller
    $car = new Car();
    $car->id = 1; 
    $car->car_at = "car";

    $car2 = new Moped();
    $car2->id = 2; 
    $car2->moped_at = "moped";

    $saloon = new Saloon();
    $saloon->veichle = $car2;
    $json = $this->get('jms_serializer')->serialize($saloon,'json');
    $saloon_des = $this->get('jms_serializer')->deserialize($json,'...\Saloon','json');
    //Error: The discriminator field name "type" for base-class "...\Test\Vehicle" was not found in input data. 

Any suggestion?

ghost avatar Jul 01 '14 11:07 ghost

The discriminator field will be correctly serialized if class is root class. If the class is a property of another class, discriminator field is not serialized. Any suggestion?

scasei avatar Jul 03 '14 09:07 scasei

+1

danieledangeli avatar Jul 03 '14 09:07 danieledangeli

I have to ask this question, too: any progress on this issue? We recently ran into the same issue, that our discriminated fields are properly deserialized but are missing upon serialization (serialization to JSON results in an empty object of that property).

immutef avatar Jul 14 '14 09:07 immutef

After some investigation, it seems the serialization process does not recognize the discrimination properly.

When serializing the concrete class, the visitor visits the concrete class configuration. But when serializing the class which embeds the discriminated object, only the visitor for the abstract class is triggered.

I think this is a bug, because when deserializing it is working as intended. I just don't know if that's "another" bug which has not much to do with the one described in the ticket ;)

immutef avatar Jul 14 '14 10:07 immutef

found out that serialization of discriminator field stops, when abstract base class is extended from a higher class itself


class base {}

abstract class user extends base {}

class admin extends user {}
class worker extends user {}

when serializing worker or admin there is no serialization of discriminator field. if class user is not extended from base, then the discriminator field will be serialized.

scasei avatar Jul 24 '14 08:07 scasei

i made a patch: https://github.com/scasei/serializer/commit/bd09a4d41a24b312a49e75d8e302dcce06df90c5

and a pull request: https://github.com/schmittjoh/serializer/pull/309

scasei avatar Jul 25 '14 08:07 scasei

You can, In fact get the discriminator value while serializing your objects using a virtual property but the getter should be named differently, here is an example:


use JMS\Serializer\Annotation as JMS;

/**
 * @JMS\Discriminator(field = "objectType", map = {
 * "file": "File"
 * })
 */
abstract class ObjectAbstract {

    /**
     * @JMS\Type("integer")
     */
    public $id;    

    /**
     * @JMS\VirtualProperty
     * @JMS\SerializedName("objectType")
     */
    abstract public function getType();
}

class File extends ObjectAbstract {

    /**
     * @JMS\Type("string")
     */
    public $fileUrl;

    /**
     * @JMS\Type("string")
     */
    public $mimeType;

    public function getType() {
        return 'file';
    }
}

It's a workaround, but hey! It work ;)

guillemcanal avatar Aug 11 '14 16:08 guillemcanal

+1 same problem for me when i serialize an entity who include a property of type AbstractDiscriminatorEntity

@guillemcanal can you please try your solution in a parent concret class who take a class ObjectAbstract property, doesn't work for me (us)

Any solution ? maybe event pre/post serialize can do the job?

yohannpoli avatar Aug 14 '14 16:08 yohannpoli

Any news on this issue?.

A precision, when i remove the type property in my yml config serializer, the serialize method work fine, unserialize does not.

If i use type in yml config, unserialize work, nut serialize fail.

Any help would be welcome.

yohannpoli avatar Aug 28 '14 16:08 yohannpoli

I've been busy the last couple weeks, I'll do a Gist to demonstrate the workaround. I'm actually using discriminator to produce a json activitystream.

guillemcanal avatar Aug 29 '14 08:08 guillemcanal

Hi I also faced with this issue. As a workaround i use such a listener

namespace Acme\Bundle\DemoBundle\EventListener;

use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\Events;
use JMS\Serializer\EventDispatcher\PreSerializeEvent;

class JMSSerializerListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [
            ['event' => Events::PRE_SERIALIZE, 'method' => 'onPreSerialize']
        ];
    }

    /**
     * @param PreSerializeEvent $event
     */
    public function onPreSerialize(PreSerializeEvent $event)
    {
        $object = $event->getObject();
        if (is_object($object) &&
            is_subclass_of($object, 'Acme\Bundle\DemoBundle\Entity\ParentEntity') &&
            get_class($object) !== $event->getType()['name']
        ) {
            $event->setType(get_class($event->getObject()));
        }
    }
}

With this listener it works fine.

molchanoviv avatar Sep 02 '14 12:09 molchanoviv

i've the same issue.

timglabisch avatar Jan 08 '15 21:01 timglabisch

@molchanoviv's solution works well for me

timglabisch avatar Jan 08 '15 21:01 timglabisch

yap that works, i even improved it with a tagging interface, so i only have to listen for an interface

digitalkaoz avatar Mar 25 '15 12:03 digitalkaoz

Hi i have the same problem the discr property is not in the serialized XML ... can someone tell me how i have to format the virtual property so it gets loaded. If i only write virtual property with the name "discr" with a value the deserializer didnt get it... i have tried each "solution" i found here ... pls help /**

  • @Discriminator(field = "type", map = {"car": "MyME\HelloBundle\Controller\Car", * "moped":"MyME\HelloBundle\Controller\Moped"}) */ abstract class Vehicle {

    public $id;

} class Car extends Vehicle {

/**
 *@Type("integer")
*/
public $id;
/**
 *@Type("string")
 */
public $car_at;

} class Moped extends Vehicle {

/**
 *@Type("integer")
 */
public $id;
/**
 *@Type("string")
 */
public $moped_at;

}

vdzpeter avatar Jun 24 '15 08:06 vdzpeter

The discriminator field name "discr" for base-class "\Entity\Content\Content" was not found in input data.

vdzpeter avatar Jun 24 '15 08:06 vdzpeter

class DiscriminatorSerializerListener implements EventSubscriberInterface
{
    /**
     * @inheritdoc
     */
    public static function getSubscribedEvents()
    {
        return [
            ['event' => Events::PRE_SERIALIZE, 'method' => 'onPreSerialize'],
        ];
    }

    /**
     * @param PreSerializeEvent $event
     */
    public function onPreSerialize(PreSerializeEvent $event)
    {
        $object = $event->getObject();

        if (is_object($object) && ($object instanceof Discriminatorable) && get_class($object) !== $event->getType()['name']) {
            $event->setType(get_class($event->getObject()));
        }
    }
}

only give your parent class the Discriminatorable interface

digitalkaoz avatar Jun 24 '15 08:06 digitalkaoz

the serialization works fine but the deserialization stillt does not work -.- The discriminator field name "discr" for base-class "\Entity\Content\Content" was not found in input data.

vdzpeter avatar Jun 24 '15 09:06 vdzpeter

yeah the discrimnator field must be serialized into the data...thats the listener for...then the deserialization works fine

digitalkaoz avatar Jun 24 '15 09:06 digitalkaoz

As i debug the serialization, it never gets to the point $event->setType(get_class($event->getObject())); are there any other solutions ?

vdzpeter avatar Jun 24 '15 10:06 vdzpeter

what ist the type you want to serialize?

vdzpeter [email protected] schrieb am Mi., 24. Juni 2015 12:44:

As i debug the serialization, it never gets to the point $event->setType(get_class($event->getObject())); are there any other solutions ?

— Reply to this email directly or view it on GitHub https://github.com/schmittjoh/JMSSerializerBundle/issues/292#issuecomment-114820331 .

digitalkaoz avatar Jun 24 '15 12:06 digitalkaoz

i want to serialize a page class. The content of this class is linked with the class content which is a abstract class for multiple modules. if i exclude the content everything works fine. if i serialize one page with content@MaxDepth(1) i get:

The discriminator field name "discr" for base-class "Basecom\Bundle\AppFrameworkBundle\Entity\Content\Content" was not found in input data.

while deserializing.

if im going to set the MaxDepth to 3 i cant even serialize it with the error : Catchable Fatal Error: Argument 2 passed to JMS\Serializer\Handler\ArrayCollectionHandler::serializeCollection() must implement interface Doctrine\Common\Collections\Collection, instance of Proxies__CG__\Entity\Page given

Any solution ?

vdzpeter avatar Jun 24 '15 13:06 vdzpeter