Jackson adds a custom field with a hash of another field
So the title basically describes what I need. Let's say the serial bean looks like this:
public class SomeBean {
public String someString;
}
I would like Jackson to serialize the SomeBean instance like this:
{
someString: '<the value>',
__hash_someString: '<a proprietary hash of <the value>>'
}
This functionality should be shared. I don't want to write a specific serializer for SomeBean because it has to happen in multiple places. It is not possible to add ' __ hash_someString ' to the class itself, because that will pollute the model.
Implementation
I would like Jackson to handle the bean normally. But when it comes across a specific annotation ( @GenerateHash ), it has to add another field to the object like before. So he would like to do this:
public class SomeBean {
@GenerateHash
public String someString;
}
So far the road
There are many similar topics, but none of them are trying something similar. I am not involved in the inner workings of Jackson Serialization, but it seems that you only get the ability to modify the object as a whole. I haven't found a way to intercept the serialization process of a field, just the value of that field.
I tried to implement this with BeanSerializerModifier and also tried some things with @Serializer. However, I usually end up with an endless loop.
The resources I have consulted (not only):
- Jackson: how to add custom property to JSON without changing POJO
- Jackson is a custom serializer that only overrides certain fields
- Custom Jackson JSON serialization for specific fields
- How to call the default deserializer from a custom deserializer in Jackson
- http://techtraits.com/programming/2011/11/21/using-custom-serializers-with-jackson/
In short How can I get Jackson to serialize
public class SomeBean {
@GenerateHash
public String someString;
public String unaffectedString;
}
:
{
someString: '<the value>',
__hash_someString: '<a proprietary hash of <the value>>',
unaffectedString: '<some value>'
}
source to share
This is pretty interesting. I think you can solve this with BeanSerializerModifier
.
The idea is to register a custom serializer that will have access to the original bean serializer, property description and object value. If a property is annotated with an annotation GenerateHash
, then the serializer issues an additional field. Here's 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}
source to share