Для меня лучшее решение - использовать @JsonView
и создавать специальные фильтры для каждого сценария.Вы также можете использовать @JsonManagedReference
и @JsonBackReference
, однако это жестко закодированное решение только для одной ситуации, когда владелец всегда ссылается на сторону владельца, а не наоборот.Если у вас есть другой сценарий сериализации, где вам нужно по-другому аннотировать атрибут, вы не сможете.
Задача
Позволяет использовать два класса, Company
и Employee
, гдемежду ними есть циклическая зависимость:
public class Company {
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
public class Employee {
private Company company;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
И тестовый класс, который пытается сериализоваться с использованием ObjectMapper
( Spring Boot ):
@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
public class CompanyTest {
@Autowired
public ObjectMapper mapper;
@Test
public void shouldSaveCompany() throws JsonProcessingException {
Employee employee = new Employee();
Company company = new Company(employee);
employee.setCompany(company);
String jsonCompany = mapper.writeValueAsString(company);
System.out.println(jsonCompany);
assertTrue(true);
}
}
Еслизапустив этот код, вы получите:
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
Решение Использование `@ JsonView`
@JsonView
позволяет использовать фильтры и выбирать, какие поля следует включать при сериализацииобъекты.Фильтр - это просто ссылка на класс, используемая в качестве идентификатора.Итак, давайте сначала создадим фильтры:
public class Filter {
public static interface EmployeeData {};
public static interface CompanyData extends EmployeeData {};
}
Помните, что фильтры - это фиктивные классы, просто используемые для указания полей с аннотацией @JsonView
, так что вы можете создавать столько, сколько вам нужно и нужно.Давайте посмотрим на это в действии, но сначала нам нужно аннотировать наш Company
класс:
public class Company {
@JsonView(Filter.CompanyData.class)
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
и изменить тест для того, чтобы сериализатор использовал View:
@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
public class CompanyTest {
@Autowired
public ObjectMapper mapper;
@Test
public void shouldSaveCompany() throws JsonProcessingException {
Employee employee = new Employee();
Company company = new Company(employee);
employee.setCompany(company);
ObjectWriter writter = mapper.writerWithView(Filter.CompanyData.class);
String jsonCompany = writter.writeValueAsString(company);
System.out.println(jsonCompany);
assertTrue(true);
}
}
Теперь, если вы запустите этот код, проблема бесконечной рекурсии будет решена, потому что вы прямо сказали, что хотите просто сериализовать атрибуты, которые были помечены @JsonView(Filter.CompanyData.class)
.
Когда он достигает обратной ссылки для компании вEmployee
, он проверяет, что он не аннотирован, и игнорирует сериализацию.У вас также есть мощное и гибкое решение, позволяющее выбирать, какие данные вы хотите отправлять через API REST.
С помощью Spring вы можете аннотировать ваши методы REST Controllers с помощью желаемого фильтра @JsonView
, и сериализация прозрачно применяется квозвращаемый объект.
Вот импорт, используемый в случае, если вам нужно проверить:
import static org.junit.Assert.assertTrue;
import javax.transaction.Transactional;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.annotation.JsonView;