How to Use Jackson Beandeserializermodifier?

I am trying to implement a custom deserializer. Because I only want to add functionality to the default deserializer, I tried to store in my custom deserializer the default one: I would like to use the default to deserialize the json and then add other information.

I am trying to use BeanDeserializerModifier to register the custom deserializer.

SimpleModule module = new SimpleModule("ModelModule", Version.unknownVersion());

module.setDeserializerModifier(new BeanDeserializerModifier() {
    @Override
    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
        JsonDeserializer<?> configuredDeserializer = super.modifyDeserializer(config, beanDesc, deserializer);
        if (Document.class.isAssignableFrom(beanDesc.getBeanClass())) {
            logger.debug("Returning custom deserializer for documents");

            configuredDeserializer = new DocumentDeserializer(configuredDeserializer, (Class<Document>)beanDesc.getBeanClass());
        } 

        return configuredDeserializer;
    }
});

As you can see, if the object to generate is a "Document", I am modifying the deserializer returning a custom deserializer. I am passing the default deserializer to the constructor so I can use it later.

When I try to deserialize, Jackson fails with the error:

No _valueDeserializer assigned(..)

I have investigated and it seems that the default deserializer does not have the correct deserializers for its properties: for all the properties, it is using the deserializer FailingDeserializer that, of course, fails and returns the error mentioned above. This deserializer is supposed to be substituted but it is not.

It seems that, after calling the method modifyDeserializer, Jackson completes the configuration.

The custom deserializer that I am using is:

@SuppressWarnings("serial")
public class DocumentDeserializer extends StdDeserializer<Document> {
    private JsonDeserializer<?> defaultDeserializer;
    private DocumentDeserializer(JsonDeserializer<?> defaultDeserializer, Class<? extends Document> clazz) {
        super(clazz);
        this.defaultDeserializer = defaultDeserializer;
    }

    @Override
    public Document deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        Document documentDeserialized = (Document) defaultDeserializer.deserialize(jp, ctxt);
        /* I want to modify the documentDeserialized before returning it */
        return documentDeserialized;
    }
}

UPDATE: I solved the problem using a different Deserializer:

public class CustomDeserializerModifier extends BeanDeserializerModifier {
    private static final Logger logger = Logger.getLogger(CustomDeserializerModifier.class);

    public CustomDeserializerModifier (Factory factory) {
        this.factory = factory;
    }

    @Override
    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
        JsonDeserializer<?> configuredDeserializer;
        if (CustomDeserializedNode.class.isAssignableFrom(beanDesc.getBeanClass())) {
            Converter<Object, Object> conv = beanDesc.findDeserializationConverter();
            JavaType delegateType = conv.getInputType(config.getTypeFactory());
            configuredDeserializer = new CustomDeserializedNodeDeserializer(conv, delegateType, (JsonDeserializer<Document>) deserializer, 
                    (Class<? extends CustomDocument<?>>)beanDesc.getBeanClass());
        } else {
            configuredDeserializer = super.modifyDeserializer(config, beanDesc, deserializer);
        }

        return configuredDeserializer;
    }

    @SuppressWarnings("serial")
    public class CustomDeserializedNodeDeserializer extends StdDelegatingDeserializer<Object> {
        private Class<? extends CustomDocument<?>> beanClass;
        public CustomDeserializedNodeDeserializer(Converter<Object,Object> converter,
                JavaType delegateType, JsonDeserializer<Document> delegateDeserializer, Class<? extends CustomDocument<?>> beanClass) {
            super(converter, delegateType, delegateDeserializer);
            this.beanClass = beanClass;
        }

        @Override
        public CustomDeserializedNode deserialize(JsonParser jp, DeserializationContext ctxt)

                throws IOException, JsonProcessingException {
            CustomDeserializedNode node = (CustomDeserializedNode)factory.createCustomDocument(beanClass);
            CustomDeserializedNode documentDeserialized = (Document) super.deserialize(jp, ctxt, node);

            return documentDeserialized;
        }
    }

}

Probably extending StdDelegatingDeserializer does what @StaxMan is suggesting.

1 Answer

This should be added in a FAQ, but what you need to do is to implement 2 interfaces:

  • ResolvableDeserializer (method resolve(...))
  • ContextualDeserializer (method createContextual(...))

and delegate these calls to defaultDeserializer in case it implements one or both interfaces. These are required for deserializer initialization; especially ContextualDeserializer through which property annotations are made available to deserializers. And ResolvableDeserializer is used by BeanDeserializer to get deserializers for properties it has, if any; this is where _valueDeserializer in question is likely to be fetched.

3

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Chloe Bennett

Chloe Bennett

Culture, Media & Entertainment Columnist

Chloe Bennett explores the intersection of pop culture, streaming entertainment, digital trends, and contemporary lifestyle. Her weekly commentary reaches thousands of culture enthusiasts.

Share this article
Twitter Facebook Pinterest