I am in dire need of help. I am currently making some security restrictions on a resource in a Content API, where i need to either: Include properties, Truncate properties (if they are String.class) or remove properties from the object of serialization, based on a SecurityContext.
Im using Jackson as my json serializer, and have chosen to use a BeanPropertyFilter to check if each property should be serialized or not.
Now the remove, and include options are pretty straight forward, either i serialize the bean or not.
But the truncate option is a little worse. So here's what ive done so far regarding the serialization of the property.
@Override
public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider provider, BeanPropertyWriter writer) throws Exception {
switch (determineFilterAction(writer)) {
case INCLUDE_UNMODIFIED:
writer.serializeAsField(bean, jgen, provider);
break;
case INCLUDE_TRUNCATED:
Object value = writer.get(bean);
if (!(value instanceof String)) {
throw new UnsupportedOperationException("Annotation indicates truncate on " + writer.toString() + " which is not a string, but a " + value.getClass().getSimpleName() + " and is unsupported");
}
JsonSerializer oldSerialzer= writer.getSerializer();
writer.assignSerializer(new TruncateStringJsonSerializer());
writer.serializeAsField(bean, jgen, provider);
writer.assignSerializer(oldSerialzer);
break;
case REMOVE:
// do nothing
break;
}
}
Problem is, that there's a guard in the BeanPropertyWriter.assignSerializer() method, that will not allow me to switch in a new custom serializer for that one property.
Is there any way of hooking into, and modifying the bean-value? Or will i have to override Factories, and BeanPropertyWriter in order to override the assignSerializer() method (to avoid the serializer guard)?
I cant just annotate my properties with a special writer, since the object of serialization doesnt know anything about the SecurityContext, so there is no way to inject the securityContext into a custom serializer.
Is there any way at all to make this truncate option happen (without making it too hackish)?
Thanks in advance.
//Best regards, Martin.
UPDATE
Based on StaxMan's answer, i have updated the method to look like this, and it seems to work. At least all my tests run correctly. Will test further on development application-servers.
@Override
public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider provider, BeanPropertyWriter writer) throws Exception {
switch (determineFilterAction(writer)) {
case INCLUDE_UNMODIFIED:
writer.serializeAsField(bean, jgen, provider);
break;
case INCLUDE_TRUNCATED:
Object value = writer.get(bean);
if (!(value instanceof String)) {
throw new UnsupportedOperationException("Annotation indicates truncate on " + writer.toString() + " which is not a string, but a " + value.getClass().getSimpleName() + " and is unsupported");
}
String valueString = (String) value;
jgen.writeFieldName(writer.getName());
jgen.writeString(StringUtils.abbreviate(valueString, MAX_LENGTH));
break;
case REMOVE:
// do nothing
break;
}
}
No, do not even try to call assignSerializer
: that is a bad idea in multi-threaded environment -- there is just a single instance of that serializer, and even if you could call it, it'd just cause you a bizarre exception in cases where two serializations were done concurrently.
But perhaps you should consider approaching this from different direction: since you are already finding String
value, why not directly write truncated value using JsonGenerator
?
Or, if you prefer, delegate to something that does the write; but since you are calling it, it need not be a JsonSerializer
or anything.