Сериализация/десериализация унаследованного класса с помощью JMS Seralizer

Чтобы все было просто, у меня есть

/*
 * @JMS\Discriminator(field = "type", map = {
 *     "one": "...\ItemOne",
 *     "two": "...\ItemTwo"
 * })
 */
Abstract Class Item {}
.
.
.
class ItemOne extends Item {/* Contains multiple properties */}
class ItemTwo extends Item {/* Contains multiple properties */}

class Example
{
      /**
        * @var Item[] $items
        * 
        * @ORM\OneToMany(targetEntity="...\Items", mappedBy="holder",cascade={"persist", "remove"})
        */
      protected $items;
}

Как только я сериализую этот объект с помощью FOSRestBundle, все выглядит хорошо, я получаю массив элементов, и каждый элемент будет иметь поле дискриминатора, но после десериализации я получаю следующую ошибку

"You must define a type for ...\Example::$items."

Итак, чтобы решить эту проблему, я добавил

/**
  * @ORM\...
  * @JMS\Type("ArrayCollection<...\Item>")
  */
protected $items;

На этот раз сериализованный $items, похоже, ограничен родительским классом Item, все объекты потеряют все свойства, связанные с дочерними элементами ItemOne и ItemTwo, и будут сериализованы без какого-либо поля дискриминатора, поэтому десерализация завершится ошибкой.

"The discriminator field name "type" for base-class "...\Item" was not found in input data."

Как я могу решить эту проблему?

ИЗМЕНИТЬ, поскольку @erlangb предположил, что это распространенная проблема в JMS Serializer https://github.com/schmittjoh/JMSSerializerBundle/issues/292

хотя я не смог заставить это работать, используя упомянутый обходной путь

/**
 * @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');

все еще получаю

 The discriminator field name "type" for base-class "...\Test\Vehicle" was not found in input data. 

или если я удалю @JMS\Type из Saloon->veichle, я получу

The discriminator field name "type" of the base-class "...\Test\Vehicle" conflicts with a regular property of the sub-class "...\Test\Moped". 

person trrrrrrm    schedule 30.06.2014    source источник
comment
Вы нашли решение? Такая же проблема сейчас.   -  person Roel Veldhuizen    schedule 07.08.2014
comment
как обсуждается ниже, кажется, что это ошибка, пользователь scasei на github создал патч, но у меня еще не было времени его протестировать. Подробнее об этой проблеме можно прочитать здесь: github.com/schmittjoh/JMSSerializerBundle/issues/292   -  person trrrrrrm    schedule 08.08.2014


Ответы (2)


Есть известная проблема. Я столкнулся с той же проблемой несколько лет назад. Я не знаю, исправили ли они это, но посмотрите здесь: https://github.com/schmittjoh/JMSSerializerBundle/issues/292

Теперь я посмотрел, что в jms serialzier есть эта аннотация:

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

Вы можете установить виртуальное свойство, например:

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

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

Попробуйте таким образом

person erlangb    schedule 30.06.2014
comment
спасибо за ваш ответ, я пробовал таким образом, я получил следующую ошибку The discriminator field name \"type\" of the base-class \"...\Item" conflicts with a regular property of the sub-class \"...\ItemOne" - person trrrrrrm; 01.07.2014
comment
Вероятно, у вас есть элемент с именем type в ItemOne. Попробуйте удалить его или изменить тип поля дискриминатора с помощью diskr и метода getType в getDiscr. - person erlangb; 01.07.2014
comment
Спасибо за вашу помощь, я попытался использовать тот же упомянутый пример и все еще получаю ту же проблему, я делаю что-то не так? пожалуйста, проверьте мое редактирование, большое спасибо! - person trrrrrrm; 01.07.2014
comment
тот же The discriminator field name "type" of the base-class "Entity\Test\AbstractUser" conflicts with a regular property of the sub-class "Entity\Test\User" - person trrrrrrm; 01.07.2014
comment
Хорошо, попробуйте удалить тип свойства из абстрактного класса. Объект будет сериализован. Он также хорошо работает в десериализации: - person erlangb; 01.07.2014
comment
Это суть, дайте мне знать, если она работает: D а> - person erlangb; 01.07.2014
comment
Спасибо за ваше время, я обновил вашу суть до gist.github.com/laithar/9e4c3641d0dbfa3c1f02 Address серализация/десерализация не сработает - person trrrrrrm; 02.07.2014
comment
Да, я знаю, это именно та проблема, которую я прокомментировал на git hub. Я постараюсь найти решение или обходной путь для этого, потому что мне это тоже интересно. - person erlangb; 02.07.2014
comment
да, я постараюсь найти решение для этого, я думаю, проблема в аннотации Type, поскольку она работает, если вы напрямую сериализуете объект, в любом случае буду держать вас в курсе, если я найду решение для этого, большое спасибо за вашу помощь ! - person trrrrrrm; 02.07.2014

/**
 * @JMS\Discriminator(field = "type", map = {
 *    "user": "User",
 *    "mod": "Moderator"
 * })
 */
abstract class AbstractUser
{
    /**
     * @JMS\Type("integer")
     */
    public $id;

    /**
     * @JMS\Type("string")
     */
    public $name
}

class Moderator extends AbstractUser
{
    /**
     * @JMS\Type("string")
     */
    public $foo;
}

class User extends AbstractUser
{
    /**
     * @JMS\Type("string")
     */
    public $bar;
}

Это мой тест:

public function testSerializer()
{
    $data = new Moderator();
    $data->foo = 'foo';
    $data->name = 'daniele';
    $serializer = SerializerBuilder::create()->build();
    $jsonContent = $serializer->serialize($data, 'json');

    $obj = $serializer->deserialize($jsonContent, '..MyBundle\...\AbstractUser',     json');
    $this->assertTrue(get_class($obj), 'MyBundle....\Moderator');

}

Я упростил пространство имен, но оно работает

person erlangb    schedule 01.07.2014
comment
Я очень ценю вашу щедрую помощь! ваш пример работает отлично, но можете ли вы попытаться создать новый класс, например, Address с общедоступным свойством user, и добавить $data из вашего примера в экземпляр Address, как только вы сериализуете адрес, type больше не будет в сериализованном json. в моем примере, если я сериализую Car, это работает, но как только я делаю Saloon, это не так. - person trrrrrrm; 01.07.2014