I'm using SpringBoot / Hibernate for my application framework. I have a class that holds a load of parameters for my application. It has some parameters that always apply, and some that differ depending on what's being worked on (per-project params). This has been modelled as a Parent class that contains a collection of Child classes, whereby one and only one instance of the children will be 'active' at any one time, depending on some context (e.g. the project being worked on, but the real case has somewhat more complex logic to determine the 'active' child).
In nearly all cases, I only want to serialise the one child that is applicable at the time, but in a few cases, I want to serialise the whole set.
I can achieve it using the following:
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;
}
Then in my controller I can do:
@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;
}
Works nicely, but introduces some state (setting the context on the Parent) that I want to avoid.
So now the question: How can I serialise the Parent / child classes to remove the state attribute, e.g. something like:
@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;
}
Everything I've found about the Json Context serialisation seems to have a fixed set of contexts (mines an unknown list) and be used to add/remove attributes from the output, rather than filtering the data.
The only solution I've found so far is to call ObjectMapper myself to get a full json serialisation of all the children, then remove all but the one I want to return. But doing that means I have duplicate sets of "Which child is active for this context" logic - one within the Parent class the works with the Parent and Child objects, and one in the getParentWithContext function that works with the json representation. Is there anything better I can do, e.g.
- Do some custom serialiser that can somehow call the getActiveChild method on the parent with the passed context before rendering the result, or
- Somehow make the context ThreadLocal when it's set in the rendering call?
- Somehow create a new class that extends my Parent class with the Context class instance in its constructor and return that for Jackson to serialize?
- Create a clone of the Parent, set the context on the clone, let that be serialised then garbage-collected when done?