Reputation: 247
So, the title basically describes what I need. Say, the bean to be serialized looks like this:
public class SomeBean {
public String someString;
}
I would like Jackson to serialize an instance of SomeBean like this:
{
someString: '<the value>',
__hash_someString: '<a proprietary hash of <the value>>'
}
This functionality should be generic. I don't want to write a specific serializer for SomeBean because this needs to happen at multiple locations. It is not an option to add the '__hash_someString' to the class itself because it would pollute the model.
Implementation
I would like Jackson to process the bean normally. But when it encounters a specific annotation (@GenerateHash) it should add another field to the object like before. So it would like this:
public class SomeBean {
@GenerateHash
public String someString;
}
The road so far
There are a lot of similar topics but none of them attempt something like this. I'm not really into the inner workings of Jackson Serialization but it seems you only get the option of modifying an object as a whole. I haven't found a way to intercept the serialization process of a field, only the value of that field.
I've tried to implement this using a BeanSerializerModifier and also tried some things with @Serializer. However, I usually end up in a infinite loop.
Resources I consulted are (not limited to):
In short How can I get Jackson to serialize
public class SomeBean {
@GenerateHash
public String someString;
public String unaffectedString;
}
to this:
{
someString: '<the value>',
__hash_someString: '<a proprietary hash of <the value>>',
unaffectedString: '<some value>'
}
Upvotes: 5
Views: 2800
Reputation: 10853
This is quite interesting. I think you can solve this with BeanSerializerModifier
.
The idea is to register a custom serialiser which would have access to the original bean serialiser, the property description and the object value. If the property is annotated with a GenerateHash
annotation, then the serialiser will emit an additional field. Here is an example:
public class JacksonGenerateHash {
@Retention(RetentionPolicy.RUNTIME)
public static @interface GenerateHash {
}
public static class Bean {
@GenerateHash
public final String value;
public Bean(final String value) {
this.value = value;
}
}
private static class MyBeanSerializerModifier extends BeanSerializerModifier {
@Override
public JsonSerializer<?> modifySerializer(
final SerializationConfig serializationConfig,
final BeanDescription beanDescription,
final JsonSerializer<?> jsonSerializer) {
return new HashGeneratingSerializer((JsonSerializer<Object>) jsonSerializer, null);
}
}
private static class HashGeneratingSerializer extends JsonSerializer<Object>
implements ContextualSerializer {
private final JsonSerializer<Object> serializer;
private final BeanProperty property;
public HashGeneratingSerializer(
final JsonSerializer<Object> jsonSerializer,
final BeanProperty property) {
this.serializer = jsonSerializer;
this.property = property;
}
@Override
public void serialize(
final Object o,
final JsonGenerator jsonGenerator,
final SerializerProvider serializerProvider)
throws IOException {
serializer.serialize(o, jsonGenerator, serializerProvider);
// if the generatehash is present the property must be set
if (property != null) {
jsonGenerator.writeNumberField("_hash_" + property.getName(), o.hashCode());
}
}
// override this method to access the bean property
@Override
public JsonSerializer<?> createContextual(
final SerializerProvider prov, final BeanProperty property)
throws JsonMappingException {
if (property != null && property.getAnnotation(GenerateHash.class) != null) {
return new HashGeneratingSerializer(serializer, property);
}
return serializer;
}
}
public static void main(String[] args) throws JsonProcessingException {
SimpleModule module = new SimpleModule();
module.setSerializerModifier(new MyBeanSerializerModifier());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
System.out.println(mapper.writeValueAsString(new Bean("abc")));
}
}
Output:
{"value":"abc","_hash_value":96354}
Upvotes: 6