2

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;
    }
}
Martin Hansen
  • 2,033
  • 1
  • 18
  • 34
  • If i could just override the default BeanPropertyWriter that jackson initializes, with my own, and register it, it could override the guard that prevents me from switching out the serializers. But i cant find anyone how has done this on the web. (At least not without implementing own, factory provider AND own factory). Wich im not too fond of. – Martin Hansen Oct 08 '14 at 09:30

1 Answers1

2

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.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • Thanks for replying. This helped, and worked. Its a tad ugly, just doing a manual write, i would have prefered to let the jackson setup handle it all. Good to know about the assignSerializer, i thought jackson could handle this, when it can handle switching out serializers with @JsonSerialize(using = SomeCustomSerializer.class).. There's even a note in the source code, commenting on weather or not to remove the guard in a future version. :) – Martin Hansen Oct 09 '14 at 06:44
  • Yeah, it is not always clear what can and especially what should be done. Maybe that method should have a warning note. Plus basic `JsonSerializer` and `JsonDeserializer` should emphasize the fact that instances need to be thread-safe, as instances are shared for concurrent use. – StaxMan Oct 09 '14 at 17:27