Как обрабатывать рекурсивные объекты с помощью сериализатора JMS
Я пытаюсь сериализовать и десериализовать граф объектов доктрины.
Структура довольно сложная, но этот пример подводит итог моей проблеме:
Существует сущность Company
, имеющая отношение к одному множеству Employee
.
Сущность Employee
имеет многоуровневую связь с Company
.
Это сериализуется следующим образом:
{
"company": {
"name": "MegaCorp",
"employees": [{
"name": "John Doe",
"company": null
}]
}
}
Таким образом, это null
ссылка на родителя Employee
Company
. Для сериализации это нормально.
Но теперь, когда я десериализую этот json, Я получаю null
Company
в объекте Employee
. Чего я хочу (и ожидаю), так это получить правильную ссылку на родителя Company
.
Возможно ли это с помощью сериализатора JMS, и если да, то как это можно сделать?
Если это невозможно, что может быть хорошим обходным путем? Помните, что это большой график, я не хочу делать это вручную.
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
, вы получили бы тот же результат.
Как насчет установки ссылки вручную, когда вам нужно десериализовать объект? Что-то вроде этого:
class Company {
....
@PostDeserialize
public function setReferences()
{
foreach( $this->employees as $employee ){
$employee->setCompany( $this );
}
}
}
Это хорошая практика, чтобы максимально упростить ваши сущности 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"
}
]
}
]
}