Velvet Star Monitor

Standout celebrity highlights with iconic style.

general

How to use Jackson BeanDeserializerModifier?

Writer Andrew Mclaughlin

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

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

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.