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

Created on 3 May 2013  Â·  74Comments  Â·  Source: schmittjoh/JMSSerializerBundle

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 "MyMEHelloBundleControllerVehicle" was not found in input data

serializer issue

All 74 comments

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.

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

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?

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

Look at the @SerializerVirtualProperty 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';
    }
}

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

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.

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?

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?

+1

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).

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 ;)

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.

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 ;)

+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?

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.

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.

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.

i've the same issue.

@molchanoviv's solution works well for me

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

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": "MyMEHelloBundleControllerCar", * "moped":"MyMEHelloBundleControllerMoped"})
    */
    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;

}

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

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

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

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

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

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
.

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 "BasecomBundleAppFrameworkBundleEntityContentContent" 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 JMSSerializerHandlerArrayCollectionHandler::serializeCollection() must implement interface DoctrineCommonCollectionsCollection, instance of Proxies__CG__EntityPage given

Any solution ?

try to set the type for the discriminated model explicitly... @Type('Your\Abstract\Class')

now the

The discriminator field name "discr" for base-class "EntityContentContent" was not found in input data.
error is left -.-

The discr is listet in the content entry but it gets not deserialized

<entry> 

    <show_counter_in_title> false </show_counter_in_title>

    <discr><![CDATA[listmodule]]></discr>

</entry>

any ideas ?

mh as i see you named the discriminator column type in your serialized data its discr

and thats a problem ?

mh yes i think so...how should the serializer know that thats your discriminator column?

how can i declarate it that the serializer knows it ?

rename type to discr

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

thats what i did and it does not work -.-

@JMSDiscriminator(field = "discr", map = {

ah Vehicle should implement Discriminatorable if you use my listener

yep i did that too

then it should work, but to debug we need a complete example of your relevant model parts and your serialized data

can i solve it with a virtual property ? my question is how should the virtual property then look like ?

the listener ist registered etc? can you die before event->setData ?

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()));
        }
}

As i debug, it comes until the if query but never enters it

which condition fails?

get_class($object) !== $event->getType()['name']

what object do you try to deserialize?

i got a page which has content as a child and content is a abstract class for modules .
i now want to serialize a page with the hole content in it that includes child pages which also can have content.

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

thats the error i get

urgz seriously your example shows something about cars and mopeds now you talk about pages...you speak about deserialization and serialization at the same time...im lost and confused...

i only wanted to make clear what's my problem. So my project:
Page
Content(abstract)
Module 1 | Module 2 | Module 3

i now want to serialize AND deserialize a page with the hole content in it that includes child pages which also can have content. At the deserialization the error :
"The discriminator field name "discr" for base-class "EntityContentContent" was not found in input data." occurs. I hope its more obvious now

show some relevant Code and serialized Data...Pseudo UMLish is not very helpful here

the serialized XML output
bildschirmfoto 2015-06-25 um 10 47 55

as you can see it adds a discr field bur while deserializing it fires the error that it could not found

We need the mapping Informations of your Model

vdzpeter [email protected] schrieb am Fr., 26. Juni 2015 09:33:

as you can see it adds a discr field bur while deserializing it fires the
error that it could not found

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

bildschirmfoto 2015-06-26 um 10 35 54
bildschirmfoto 2015-06-26 um 10 38 25

is that enough information ?

bildschirmfoto 2015-06-26 um 10 53 36

mh if i compare your mapping information with the XMLi noticed: menumodule isnt in your discriminator map. it fails for all modules?

thats nearly the same use case as in my current project, it works like a charm here...dont really see the failure

yeah it fails for all modules i dont know why... i have tried everything i found to fix it but nothing worked -.-

any other ideas for a solution ? =(

Can someone show me a XML where i can see how it has to look like ? or how i can manipulate the data to get the right output ?

@schmittjoh do u maybe have any advice how i can solve the problem?

Hi, I run to similar problem with serializing Embed documents with mongodb.

I have user which can have multiple profiles, like facebook, google,...

so I have this property with many document which can be several Classes all implementing same interface.

    /**
     * @Type("ArrayCollection<ProfileInterface>")
     *
     * @ODM\EmbedMany(
     *     strategy="set",
     *     discriminatorField="type",
     *     discriminatorMap={
     *         "facebook"="ProfileFacebook",
     *         "google"="ProfileGoogle",
     *     },
     *     defaultDiscriminatorValue="profile"
     * )
     */
    protected $profiles;

Interface has:

    /**
     * @Serializer\VirtualProperty
     * @Serializer\SerializedName("profileType")
     *
     * @return string
     */
    public function getProfileName();

    /**
     * @Serializer\VirtualProperty
     * @Serializer\SerializedName("profileLink")
     *
     * @return mixed
     */
    public function getProfileLink();

    /**
     * @Serializer\VirtualProperty
     * @Serializer\SerializedName("profileImageLink")
     *
     * @return mixed
     */
    public function getProfileImageLink();

same have Implemented classes but I can't any way get serialized profile, profile field is not even shown with serialize_null: true.

Can I somehow fix this?

Ok ! Thanks to @molchanoviv I found a full workaround (serialize / deserialize) to that issue. I created a listener which listen to both PreSerialize and PostSerialize events :

namespace App\Bundle\AppBundle\EventListener;

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

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

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

        if (is_object($object) &&
            is_subclass_of($object, 'App\Bundle\AppBundle\Entity\ParentEntity')  &&
            get_class($object) !== $event->getType()['name']
        ) {
            $event->setType(get_class($event->getObject()));
        }
    }

    /**
     * @param ObjectEvent $event
     */
    public function onPostSerialize(ObjectEvent $event){
        $object = $event->getObject();
        if (is_object($object) &&
            (is_a($object, 'App\Bundle\AppBundle\Entity\ChildEntity')
                || is_a($object, 'App\Bundle\AppBundle\Entity\ChildEntity2') ) ) {
            $event->getVisitor()->addData("serializedType", $object->getType());
        }
    }
}

Listener definition :

<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <parameters>      
        <parameter key="cfa.events.jmsserializer_listener.class">App\Bundle\AppBundle\EventListener\JMSSerializerListener</parameter>
    </parameters>

    <services>
         <service id="cfa.events.jmsserializer_listener" class="%cfa.events.jmsserializer_listener.class%">
            <tag name="jms_serializer.event_subscriber"/>
        </service>

    </services>
</container>

Where :

  • ParentEntity is abstract class with :
/**
 * @JMSSer\ExclusionPolicy("all")
 * @JMSSer\Discriminator(field="serializedType", map={
 *      "child"="App\Bundle\AppBundle\Entity\ChildEntity",
 *      "child2"="App\Bundle\AppBundle\Entity\ChildEntity2",
 * })
  • ChildEntity and ChildEntity2 extend ParentEntity

No need to use @JMSSerialize\VirtualProperty anywhere.

Maybe I miss something but it works really well like that and for both serialization and deserialization !

Is there any change this issue will be fixed?
I would help with a PR, but JMSSerializer is a black box for me at this moment. I will probably have time to fix it, but seems like the maintainers don't care about this anymore. Sorry, didn't intend to BM you guys, your project is awesome nonetheless!

UPDATE
Somehow it works. Seems like it was a caching issue. If anyone like me is experiencing this issue, I can share my working code with you, without any additional workarounds.

@meetmatt That would be great

I just run in the same issue in our project after updating jms/serializer. The problem is; the serializer does a strict checking of the provided type field and this data need to be provided. This makes sense on one hand but if you just deliver a ID for referenced data it is not necessary to deliver a type cause the referenced object is unique and the given type is implicit given.

I've just created a pull request: https://github.com/schmittjoh/serializer/pull/748
which does not disable this "feature" per se, it adds the option to manually disable the strict type checking on an discriminator map. It solves our problem 100%

Still does not work for me.
`

<class name="ProcessBundle\Entity\AbstractNode" discriminator-field-name="type" discriminator-disabled="false" exclusion-policy="ALL">

    <discriminator-class value="sendEmail">ProcessBundle\Entity\SendEmail</discriminator-class>

</class>

`

abstract class AbstractNode { }

class SendEmail extends AbstractNode { public $type = 'sendEmail'; }

Then I get:

LogicException: The discriminator field name "type" of the base-class "ProcessBundleEntity\AbstractNode" conflicts with a regular property of the sub-class "ProcessBundleEntitySendEmail"

@Hast i still not get the PullRequest trough on the serializer library... Hope i get it done by this week.

@tspycher can you answer to https://github.com/schmittjoh/serializer/pull/748#issuecomment-296214160 ?

I am getting "The discriminator map cannot be empty." error on Final review page after click on "process to payment button".

Was this page helpful?
0 / 5 - 0 ratings