Я использую SpringBoot / Hibernate для моей инфраструктуры приложений.
У меня есть класс, который содержит загрузку параметров для моего приложения. У него есть некоторые параметры, которые применяются всегда, а некоторые отличаются в зависимости от того, над чем вы работаете (параметры для каждого проекта). Это было смоделировано как родительский класс, который содержит коллекцию дочерних классов, в результате чего один и только один экземпляр дочерних элементов будет «активным» в любой момент времени, в зависимости от некоторого контекста (например, над проектом, над которым ведется работа, но с реальным case имеет несколько более сложную логику для определения «активного» потомка).
Почти во всех случаях я хочу сериализовать только одного потомка, который применим в данный момент, но в некоторых случаях я хочу сериализовать весь набор.
Я могу добиться этого используя следующее:
public class Parent {
@JsonView(JsonViews.AllChildren.class)
private Set<Child> children= new HashSet<>();
@JsonIgnore
private Context context;
public void setContext (Context context) {
this.context = context;
}
@JsonView(JsonViews.ActiveChild.class)
@JsonProperty("activeChild")
public Child getActiveChild() {
for (Child child : this.children) {
if (someFunction(this.context)) {
return child;
}
}
return null;
}
Тогда в моем контроллере я могу сделать:
@GetMapping("/parent")
@JsonView(JsonViews.AllChildren.class)
public Parent getParent() {
return ParentService.getParent();
}
@GetMapping("/parent/{context}")
@JsonView(JsonViews.ActiveChild.class)
public Parent getParentWithContext(PathVariable final String contextName)) {
Context context = ContextService.getContextByName(contextName);
Parent parent = ParentService.getParent();
parent.setContext(context);
return Parent;
}
Работает хорошо, но вводит некоторое состояние (настройка контекста в Parent), которого я хочу избежать.
Итак, теперь вопрос: как я могу сериализовать родительские / дочерние классы для удаления атрибута состояния, например, что-то вроде:
@JsonView(JsonViews.ActiveChild.class)
@JsonProperty("activeChild")
public Child getActiveChild(Context context) {
for (Child child : this.children) {
if (someFunction(context)) {
return child;
}
}
return null;
}
@GetMapping("/parent/{context}")
@JsonView(JsonViews.ActiveChild.class)
public Parent getParentWithContext(PathVariable final String contextName)) {
Context context = ContextService.getContextByName(contextName);
Parent parent = ParentService.getParent();
// How to tell the Json serialization to pass the context to getActiveChild
return Parent;
}
Все, что я обнаружил в сериализации Json Context, похоже, имеет фиксированный набор контекстов (поиск неизвестного списка) и может использоваться для добавления / удаления атрибутов из вывода вместо фильтрации данных.
Единственное решение, которое я нашел до сих пор, - это вызвать сам ObjectMapper для получения полной json-сериализации всех дочерних элементов, а затем удалить все, кроме того, которое я хочу вернуть. Но это означает, что у меня есть дубликаты наборов логики «Какой дочерний элемент активен для этого контекста» - один в классе Parent работает с объектами Parent и Child, а другой - в функции getParentWithContext, которая работает с представлением json. Есть ли что-нибудь лучшее, что я могу сделать, например,
- Сделайте какой-нибудь пользовательский сериализатор, который может каким-то образом вызвать метод getActiveChild на родительском объекте с переданным контекстом перед отображением результата, или
- Каким-то образом сделать контекст ThreadLocal, когда он установлен в вызове рендеринга?
- Каким-то образом создать новый класс, который расширяет мой родительский класс экземпляром класса Context в его конструкторе и возвращает его для сериализации Джексона?
- Создать клон Родителя, установить контекст на клоне, разрешить его сериализацию, а затем собрать мусор?