Как обрабатывать рекурсивные объекты с помощью сериализатора JMS


Я пытаюсь сериализовать и десериализовать граф объектов доктрины.

Структура довольно сложная, но этот пример подводит итог моей проблеме:

Существует сущность Company, имеющая отношение к одному множеству Employee.
Сущность Employee имеет многоуровневую связь с Company.

Это сериализуется следующим образом:

{
    "company": {
        "name": "MegaCorp",
        "employees": [{
            "name": "John Doe",
            "company": null
        }]
    }
}

Таким образом, это null ссылка на родителя Employee Company. Для сериализации это нормально. Но теперь, когда я десериализую этот json, Я получаю null Company в объекте Employee. Чего я хочу (и ожидаю), так это получить правильную ссылку на родителя Company.

Возможно ли это с помощью сериализатора JMS, и если да, то как это можно сделать?
Если это невозможно, что может быть хорошим обходным путем? Помните, что это большой график, я не хочу делать это вручную.

Author: Dennis Haarbrink, 2016-04-15

3 answers

К сожалению, при десериализации сериализатор не может узнать, являются ли объекты идентичными или фактически одним и тем же объектом. Даже если бы вы могли вложить их рекурсивно.

Но вы можете получить желаемый результат, объединив аннотацию @Accessor с некоторой бизнес-логикой. Итак, исходя из вашего примера:

class Company {
    /**
     * @Accessor(setter="addEmployees")
     */
    private $employees;

    public function addEmployee(Employee $employee)
    {
        if (!$this->employees->contains($employee)) {
            $this->employees[] = $employee;
            $employee->setCompany($this);
        }
    }

    public function addEmployees($employees)
    {
        foreach ($employees as $employee) {
            $this->addEmployee($employee);
        }
    }
}

class Employee {
    /**
     * @Accessor(setter="setCompany")
     */
    private $company;

    public setCompany(Company $company = null)
    {
        $this->company = $company;

        if ($company) {
            $company->addEmployee($this);
        }
    }
}

Я думаю, что это более естественный подход, чем использование @PostDeserialize, поскольку у вас, вероятно, уже есть некоторые из этих методов в вашем коде. Кроме того, это обеспечивает заключайте контракт в обоих направлениях, поэтому, если бы вы начали с Employee, вы получили бы тот же результат.

 2
Author: Kuba Birecki, 2016-04-15 08:36:56

Как насчет установки ссылки вручную, когда вам нужно десериализовать объект? Что-то вроде этого:

class Company { 

    ....

    @PostDeserialize
    public function setReferences()
    {
        foreach( $this->employees as $employee ){
            $employee->setCompany( $this );
        }
    }
}
 0
Author: Rafa0809, 2016-07-16 14:30:41

Это хорошая практика, чтобы максимально упростить ваши сущности JSON! Если вам приходится сталкиваться с такого рода проблемами, самое время пересмотреть свою модель данных!

Кроме того, думали ли вы об использовании HATEOAS (Гипермедиа как механизм состояния приложения) - это принцип, согласно которому гипертекстовые ссылки следует использовать для создания лучшей навигации по API. Выглядит примерно так:

{
  "id": 711,
  "manufacturer": "bmw",
  "model": "X5",
  "seats": 5,
  "drivers": [
   {
    "id": "23",
    "name": "Stefan Jauker",
    "links": [
     {
     "rel": "self",
     "href": "/api/v1/drivers/23"
    }
   ]
  }
 ]
}
 0
Author: Rafa0809, 2017-01-20 17:00:54