-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
We have a thread local builder implementation to try and reduce the impact of builder instances on the garbage collector. Much of our builder usage is through Jackson, and thus we are writing a BeanDeserializerModifier with the goal of extending the BuilderBasedDeserializer.
Ideally, we would like to reuse most of the existing functionality but the class is proving somewhat difficult to extend. First, the Object deserialize(JsonParser p, DeserializationContext ctxt)
method is declared final (as are a few others). This prevents us from subclassing and overriding the method. Other methods cannot be delegated/overridden because they are protected
or private
(e.g. _deserializeUsingPropertyBased
and vanillaDeserialize
) -- however, we can work around the protected ones by placing our class in the same package.
Our code provides a build method which accepts builder class and a consumer of an instance of that builder. This encapsulates the lifecycle of the builder into the consumer and allows the code to re-use builder instances (such builder instances are required to have a reset
method which is invoked automatically before the builder is consumed). Retrofitting the code into Jackson was not too bad for the vanilla case.
First, we replace the first branch in public final Object deserialize(JsonParser p, DeserializationContext ctxt)
:
if (p.isExpectedStartObjectToken()) {
final JsonToken t = p.nextToken();
if (_vanillaProcessing) {
return vanillaDeserializeAndFinishBuild(p, ctxt, t);
}
}
And here's our vanillaDeserializeAndFinishBuild
implementation which mostly vanillaDeserialize
from BuilderBaseDeserializer
but also includes our build logic and thus skips the finishBuild
call:
private Object vanillaDeserializeAndFinishBuild(
final JsonParser p,
final DeserializationContext ctxt,
final JsonToken t)
throws IOException {
return ThreadLocalBuilder.buildGeneric(
_threadLocalBuilderClass,
b -> {
Object bean = b;
try {
for (; p.getCurrentToken() != JsonToken.END_OBJECT; p.nextToken()) {
String propName = p.getCurrentName();
// Skip field name:
p.nextToken();
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) { // normal case
try {
bean = prop.deserializeSetAndReturn(p, ctxt, bean);
} catch (Exception e) {
wrapAndThrow(e, bean, propName, ctxt);
}
} else {
handleUnknownVanilla(p, ctxt, bean, propName);
}
}
} catch (final Exception e) {
throw new RuntimeException(e);
}});
}
We are using Jackson 2.9.2
and it looks like the method is final
in master as well. No real issue here, I think we can work around it. Mostly, wanted to point out our use case and enquire as to whether you had any tips for extending BuilderBasedDeserializer
and whether some of the restrictions could be lifted in the future. If not, any help understanding what pitfalls you see that led to restricting those methods could help us structure our code better. Finally, we're open to contributing some code to make the class more extensible, but didn't want to presume what direction you had in mind so any tips in this regard are welcome as well.