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):

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>'
}  

      

+3


source to share


1 answer


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}

      

+5


source







All Articles