What would be the best way to achieve that?
Thanks a lot for any ideas!
--
You received this message because you are subscribed to the Google Groups "jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user+unsubscribe@googlegroups.com.
To post to this group, send email to jackso...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
BeanSerializerModifier would probably not help here either because it only gets called once when constructing serializer for a type, and them it is cached (new instances may be created via ContextualSerializer, but that's still just once per property serializer is to be used for).It would allow you to wrap serializer, but not do dynamical filtering itself.
Of facilities provided, JsonFilter could help, explained f.ex here:
http://www.baeldung.com/jackson-serialize-field-custom-criteria
but otherwise what is usually done is, I think, two-pass processing: first serialize from POJO into JsonNode or Map, and then explicitly modify result (remove entries or change).
BeanSerializerModifier would probably not help here either because it only gets called once when constructing serializer for a type, and them it is cached (new instances may be created via ContextualSerializer, but that's still just once per property serializer is to be used for).It would allow you to wrap serializer, but not do dynamical filtering itself.
OK, so BeanSerializerModifier will not work here...
Of facilities provided, JsonFilter could help, explained f.ex here:
http://www.baeldung.com/jackson-serialize-field-custom-criteria
I tried the BeanSerializerModifier after reading this link :-).
I'm already using JsonFilter for something else (fields filtering base on field name like in "GET /task?fields=name,id"), but I found it a bit complex to set-up (again it needs to be dynamic) and was looking for another solution for this new filtering.
Maybe I should use JsonFilter again for this, but I was afraid of pilling up filters every time I need such a thing. Do you think would be OK?
but otherwise what is usually done is, I think, two-pass processing: first serialize from POJO into JsonNode or Map, and then explicitly modify result (remove entries or change).
The problem with this approach is that it breaks the streaming behavior of Jersey. You have to serialize the whole object in memory before modifying it and send it trough the pipe. This is what I tried initially for the name filtering.
In my case it was eliminatory because my objects are big (collections of nested objects), resulting in very high memory consumption and bad performances.
Thanks for you answer, I'll keep you informed!
--
I think there are many ways to build filters, including cases where you have a small number of filters, but each with complicated logic. One possible problem is that of passing settings/configuration into filters, regarding options/parameters set. It may be necessary to resort to using something like ThreadLocal, or, possibly `ContextAttributes` (key/value pairs you can define for ObjectReader/ObjectWriter, accessible via DeserializationContext/SerializerProvider, respectively).So that may well be your best option I think.
I think there are many ways to build filters, including cases where you have a small number of filters, but each with complicated logic. One possible problem is that of passing settings/configuration into filters, regarding options/parameters set. It may be necessary to resort to using something like ThreadLocal, or, possibly `ContextAttributes` (key/value pairs you can define for ObjectReader/ObjectWriter, accessible via DeserializationContext/SerializerProvider, respectively).So that may well be your best option I think.
I was not aware of ContextAttributes, many thanks!!
I managed to get my filtering working by transmitting the dynamic parameter to my custom serializer using the ContextAttributes; it is exactly what I was looking for :-)
One pitfall was that calling "withPerCallAttribute()" on an empty ContextAttributes (ContextAttributes.getEmpty()) is not a "mutator" as the javadoc pretends. It returns a new ContextAttributes, which took me a while to discover.
Maybe something to improve here? Either always initializing the "_nonShared" field (maybe a performance hit), or just reflect this in the documentation?
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription beanDesc, JsonDeserializer<?> deserializer);
Thanks again for you time and your precious help!
--
Not necessarily simple, but you did describe the obvious problem that once you register custom (de)serializer, that's what will be found.It would also be tricky to know what the "original" deserializer would be, considering that all deserializers are pluggable by any number of modules; and lookup is in general via callbacks (ask module if it has deserializer for given type).
But there is a way to get your deserializer used via different mechanism: `BeanDeserializerModifier`. This is post-processing that occurs after databind has found deserializer to use. What you can do from this method:public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription beanDesc, JsonDeserializer<?> deserializer);is to pass `deserializer` to custom deserializer, and then delegate to it when necessary.If you do that just make sure to delegate `createDelegate` (so need to implement `ContextualDeserializer`), and make sure to use return value (possibly a new deserializer instance).And I think you also better implement `resolve()` of `ResolvableDeserializer` similarly.It may help to look at `StdDelegatingDeserializer` which does this; it's not complicated but you need to be aware of this since not delegating these methods may lead the original deserializer configuration in incomplete state (just for certain deserializers, or annotation overrides, but when that happens it takes a while find the root cause).
Not necessarily simple, but you did describe the obvious problem that once you register custom (de)serializer, that's what will be found.It would also be tricky to know what the "original" deserializer would be, considering that all deserializers are pluggable by any number of modules; and lookup is in general via callbacks (ask module if it has deserializer for given type).
True. Even more true now that I've discovered my current implementation loose the first filter feature (the one based on names) :-)
But there is a way to get your deserializer used via different mechanism: `BeanDeserializerModifier`. This is post-processing that occurs after databind has found deserializer to use. What you can do from this method:public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription beanDesc, JsonDeserializer<?> deserializer);is to pass `deserializer` to custom deserializer, and then delegate to it when necessary.If you do that just make sure to delegate `createDelegate` (so need to implement `ContextualDeserializer`), and make sure to use return value (possibly a new deserializer instance).And I think you also better implement `resolve()` of `ResolvableDeserializer` similarly.It may help to look at `StdDelegatingDeserializer` which does this; it's not complicated but you need to be aware of this since not delegating these methods may lead the original deserializer configuration in incomplete state (just for certain deserializers, or annotation overrides, but when that happens it takes a while find the root cause).
So back to BeanSerializerModifier to register my custom serialize instead of "@JsonSerialize()":
MAPPER.registerModule(new SimpleModule().setSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription desc, JsonSerializer<?> serializer) {
if (Update.class.isAssignableFrom(desc.getBeanClass())) {
return new UpdateSerializer(serializer);
}
return serializer;
}
}));
I assume that you mean `createContextual` instead of `createDelegate`, so my custom serializer is now:
public final static class UpdateSerializer extends JsonSerializer<Update> implements ContextualSerializer, ResolvableSerializer {
private final JsonSerializer<Object> delegate;
public UpdateSerializer(JsonSerializer<?> serializer) {
this.delegate = (JsonSerializer<Object>) serializer;
}
@Override
public void serialize(Update value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
Object maxTimeObj = provider.getAttribute(UPDATE_MIN_TIMESTAMP_PARAMETER);
if (maxTimeObj == null || (value.getTimestamp() > (long) maxTimeObj)) {
delegate.serialize(value, jgen, provider);
}
}
@Override
public void resolve(SerializerProvider provider) throws JsonMappingException {
if (delegate instanceof ResolvableSerializer) {
((ResolvableSerializer) delegate).resolve(provider);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
JsonSerializer<?> delSer = delegate;
if (delSer instanceof ContextualSerializer) {
delSer = prov.handleSecondaryContextualization(delSer, property);
}
if (delSer == delegate) {
return this;
}
return new UpdateSerializer(delSer);
}
}
It works as expected with both filters honored, this is great! :-)
Does it looks correct to you?
Actually you can not omit serialization here: at this point a value generally has to be written (if within JSON Object), since name has been written.So you would need to serialize something here; suppression of a property (name + value) can not be achieved from within serializer itself unfortunately.