Change Json key name dynamically - Jackson - java

I have a java class like :
class TestJsonClass {
private String propertyA;
private String propertyB;
private String propertyC;
}
Now during runtime i want to give different property names for each of the property, and not a static one using #JsonProperty("sample")
How do I accomplish this? I am using Jackson library ad Spring MVC
Thanks in advance...

You can make use of Modules for this purpose. This is the easiest solutions to your problem. Here is an example:
A simple class that can carry your property-name-mappings for each request:
public class PropertyNameMapper {
// The class for which the mappings need to take place.
public Class<?> classToFilter;
// The mappings property names. Key would be the existing property name
// value would be name you want in the ouput.
public Map<String, String> nameMappings = Collections.emptyMap();
public PropertyNameMapper(Class<?> classToFilter, Map<String, String> nameMappings) {
this.classToFilter = classToFilter;
this.nameMappings = nameMappings;
}
}
A custom BeanPropertyWriter that will be used for specifying the output name for the properties.
public class MyBeanPropertyWriter extends BeanPropertyWriter {
// We would just use the copy-constructor rather than modifying the
// protected properties. This is more in line with the current design
// of the BeanSerializerModifier class (according to its documentation).
protected MyBeanPropertyWriter(BeanPropertyWriter base, String targetName) {
super(base, new SerializedString(targetName));
}
}
Now, a custom BeanSerializerModifier that is called each time to allow you to modify the serialized properties.
public class MySerializerModifier extends BeanSerializerModifier {
public List<BeanPropertyWriter> changeProperties(
SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
List<PropertyNameMapper> propertyMappings = getNameMappingsFromRequest();
PropertyNameMapper mapping = mappingsForClass(propertyMappings,
beanDesc.getBeanClass());
if (mapping == null) {
return beanProperties;
}
List<BeanPropertyWriter> propsToWrite = new ArrayList<BeanPropertyWriter>();
for (BeanPropertyWriter propWriter : beanProperties) {
String propName = propWriter.getName();
String outputName = mapping.nameMappings.get(propName);
if (outputName != null) {
BeanPropertyWriter modifiedWriter = new MyBeanPropertyWriter(
propWriter, outputName);
propsToWrite.add(modifiedWriter);
} else {
propsToWrite.add(propWriter);
}
}
return propsToWrite;
}
private List<PropertyNameMapper> getNameMappingsFromRequest() {
RequestAttributes requestAttribs = RequestContextHolder
.getRequestAttributes();
List<PropertyNameMapper> nameMappings = (List<PropertyNameMapper>) requestAttribs
.getAttribute("nameMappings",
RequestAttributes.SCOPE_REQUEST);
return nameMappings;
}
private PropertyNameMapper mappingsForClass(
List<PropertyNameMapper> nameMappings, Class<?> beanClass) {
for (PropertyNameMapper mapping : nameMappings) {
if (mapping.classToFilter.equals(beanClass)) {
return mapping;
}
}
return null;
}
}
Now, you need a custom Module to be able to customize the output using the above created BeanSerializerModifier:
public class MyModule extends Module {
#Override
public String getModuleName() {
return "Test Module";
}
#Override
public void setupModule(SetupContext context) {
context.addBeanSerializerModifier(new MySerializerModifier());
}
#Override
public Version version() {
// Modify if you need to.
return Version.unknownVersion();
}
}
Now register this module with your ObjectMapper. You can get the Jackson HTTP message converter from your spring application context, and get its object mapper.
// Figure out a way to get the ObjectMapper.
MappingJackson2HttpMessageConverter converter = ... // get the jackson-mapper;
converter.getObjectMapper().registerModule(new MyModule())
And that's it. This is the easiest way to customize serialization of your properties dynamically.
To use this, create a List of PropertyNameMappers and add it as an attribute (named "nameMappings" in this example) in the current request.
This is an example, not production-ready code. You might probably need to add null-checks and things like that. Also, a few minor adjustments might be needed based on the version of the libraries you are using.
If the solution doesn't work for you, let me know the problems you are facing.

You could inject a custom PropertyNamingStrategy into the ObjectMapper that's used in deserialization.
Just set fields into the PropertyNamingStrategy at runtime, assuming you can map them to something like the default JsonPropertyName (e.g. propertyA, propertyB, propertyC).
public class MyNamingStrategy extends PropertyNamingStrategy {
String propertyAName, propertyBName, propertyCName;
public MyNamingStrategy(String propANm, String propBNm, String propCNm) {
this.propertyAName = propANm;
//finish
}
#Override
public String nameForField(MapperConfig<?> config, AnnotatedField field,
String defaultName) {
return convert(defaultName);
}
#Override
public String nameForGetterMethod(MapperConfig<?> config,
AnnotatedMethod method, String defaultName) {
return convert(defaultName);
}
#Override
public String nameForSetterMethod(MapperConfig<?> config,
AnnotatedMethod method, String defaultName) {
return convert(defaultName);
}
public String convert(String defaultName ){
return defaultName.replace("propertyA", propertyAName).replace( //finish
}
Finally you'd create an instance and inject it at runtime.
objectMapper.setNamingStrategy(myNamingStrategyInstance));
See this Cowtowncoder post for more on PropertyNamingStrategy:
Jackson 1.8: custom property naming strategies
Or this documentation:
github.com/FasterXML/jackson-docs/wiki/PropertyNamingStrategy

Related

Jackson: How to edit existing property to the JSON without modifying the POJO?

I need to edit the name of "existing field" in POJO instead of adding "extra_field". Is it possible with the approach referenced link below?
Please note I do not want to use #JsonProperty annotation.
Requirement is, I have a POJO and want to use different field name every time without change in POJO. For example I have a field c_id in POJO and some times it need to write as cust_id and another time it would be my_id.
Also note I cannot change implementation of POJO as it is already used in several modules and have generic implementation.
POJO Example:
class MyPojo {
String id;
// getter and setters
}
Expected output can be the following: (name of field can be changed)
{"cust_id": "123"}
{"my_id": "123"}
Mixins
The easiest way to modify the output of Jackson without adding annotations to the original POJO is using mixins.
Just define a mixin-class with the necessary annotations and indicate to Jackson that you want to use the mixin when serializing the original object.
private static class MyPojoMixin {
#JsonProperty("cust_id")
private String id;
}
public String serializeWithMixin(MyPojo p) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(MyPojo.class, MyPojoMixin.class);
return mapper.writeValueAsString(p);
}
Custom property naming strategy
If you need to programmatically change the field-name, you might not be able to use the mixin solution. You could then use a custom PropertyNamingStrategy:
public class IdRenamingStrategy extends PropertyNamingStrategy {
private final PropertyNamingStrategy inner;
private final String newIdPropertyName;
public IdRenamingStrategy(String newIdPropertyName) {
this(PropertyNamingStrategy.LOWER_CAMEL_CASE, newIdPropertyName);
}
public IdRenamingStrategy(PropertyNamingStrategy inner, String newIdPropertyName) {
this.inner = inner;
this.newIdPropertyName = newIdPropertyName;
}
private String translate(String propertyName) {
if ("id".equals(propertyName)) {
return newIdPropertyName;
} else {
return propertyName;
}
}
#Override
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
return inner.nameForField(config, field, translate(defaultName));
}
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
return inner.nameForGetterMethod(config, method, translate(defaultName));
}
#Override
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
return inner.nameForSetterMethod(config, method, translate(defaultName));
}
#Override
public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam, String defaultName) {
return inner.nameForConstructorParameter(config, ctorParam, translate(defaultName));
}
}
This can be used like this:
public String serializeWithPropertyNamingStrategy(MyPojo p) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new IdRenamingStrategy("cust_id"));
return mapper.writeValueAsString(p));
}

Play Java validation fail for Set and Map

I use Play 2.5.12 to provide a web service to create an object and validate its attributes. Here is a simplified code of what I want to do:
public class Example {
private String lastName;
private List<String> firstNames;
private Map<String, Integer> vehicles;
public Example() {}
public String validate() {
if (vehicles.get("Ferrari") != null)
return "Liar!";
return null;
}
#Override
public String toString() {
return new ToStringBuilder(this).append(lastName).append(firstNames).append(vehicles).toString();
}
// getters and setters
}
public class ExampleController extends Controller {
private FormFactory formFactory;
#Inject
public ExampleController(FormFactory formFactory) {
this.formFactory = formFactory;
}
#BodyParser.Of(BodyParser.Json.class)
public Result createExample() {
Example exampleFromJackson = Json.fromJson(request().body().asJson(), Example.class);
System.out.println(exampleFromJackson.toString());
Example exampleFromForm = formFactory.form(Example.class).bindFromRequest().get();
System.out.println(exampleFromForm.toString());
// etc
return created();
}
}
If I call the web service with a body like this:
{
"lastName": "Martin",
"firstNames": ["Robert", "Cecil"],
"vehicles": {
"BMW": 1,
"Seat": 1
}
}
The deserialized object by jackson prints correctly then this error occurs:
org.springframework.beans.InvalidPropertyException: Invalid property 'firstNames[0]' of bean class [models.Example]: Property referenced in indexed property path 'firstNames[0]' is neither an array nor a List nor a Map; returned value was [[]]
If I replace the Set by a List, I can avoid this error and then I obtain the following:
models.Example#68d2162a[Martin,[Robert, Cecil],{BMW=1, Seat=1}]
models.Example#29ef1c11[Martin,[Robert, Cecil],{}]
The deserialized object by jackson is correct but the one obtained by the request isn't so I can't do a correct validation.
So my questions are:
- Why Play doesn't let me use a Set?
- Why is the Map not retrieved?

Deserialize JSON with Jackson with complex Polymorphic structure

I need help, here is the description of the task:
1. I have base interface
interface IPolymorphicModel
2. I have few nested interfaces:
interface IContentItem extends IPolymorphicModel
interface IAttachment extends IPolymorphicModel
3. I have few implementations:
public class MediaAttachment implements IAttachment
public class Article implements IContentItem {
private List<IAttachment> attachements;
}
I am using jackson (also I tried Gson using registerTypeAdapterFactory) to parse current json structure. I am using next approach for deserializing json using single Deserializer for all nested items:
Module module = new SimpleModule()
module.setDeserializerModifier(new BeanDeserializerModifier() {
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription beanDesc,
JsonDeserializer<?> deserializer){
if (IPolymorphicModel.class.isAssignableFrom(beanDesc.getBeanClass())){
return new PolymorphicJsonDeserializer()
}
return super.modifyDeserializer(config, beanDesc, deserializer);
})
But there is the problem with parsing inner items: as you can see inner object can contain object with the same super, and when jackson try to parse them it doesn't know anything about IPolymorphicModel and registering the same BeanDeserializerModifier is not possible because it will cause stack overflow.
I found the solution. And its quite easy at least in this case. To solve such kind of task, you need to use jackson TypeIdResolver. Here is an example:
1. First you need to annotate your base class, where modelType your field by which you can identify, which exactly child of base class you are using:
#JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM,
include = JsonTypeInfo.As.PROPERTY,
property = "modelType")
#JsonTypeIdResolver(IdResolver.class)
interface IPolymorphic
2. Implement your own IdResolver.class:
public class IdResolver implements TypeIdResolver {
private JavaType baseType = null;
#Override
public void init(JavaType baseType) {
this.baseType = baseType;
}
#Override
public String idFromValue(Object value) {
return idFromValueAndType(value, value.getClass());
}
#Override
public String idFromValueAndType(Object value, Class<?> suggestedType) {
String id = "";
for (Map.Entry<String, Class<? extends IPolymorphic>> entry : PolymorphicTypeRegistry.Companion.getInstance().getMap().entrySet()) {
if (entry.getValue() == suggestedType) return entry.getKey();
}
return id;
}
#Override
public String idFromBaseType() {
return idFromValueAndType(null, baseType.getRawClass());
}
#Override
public JavaType typeFromId(String id) {
Class clazz = PolymorphicTypeRegistry.Companion.getInstance().get(id);
JavaType javaType = TypeFactory.defaultInstance().constructSpecializedType(baseType, clazz);
return javaType;
}
#Override
public JavaType typeFromId(DatabindContext context, String id) {
return typeFromId(id);
}
#Override
public String getDescForKnownTypeIds() {
return "";
}
#Override
public JsonTypeInfo.Id getMechanism() {
return JsonTypeInfo.Id.CUSTOM;
}
}
In two previous steps we configure jackson to call the same deserialize behaviour for all nested classes of IPolymorphic, so the last step - we need to register deserializer for IPolymorphic:
public void configureJackson() {
ObjectMapper objectMapper = new ObjectMapper();
Module module = new SimpleModule();
module.addDeserializer(IPolymorphic.class, new PolymorphicJsonDeserializer());
objectMapper.registerModule(module);
}

Mapping URL parameters with dashes to object in Spring Web MVC

Mapping URL request parameters with Spring MVC to an object is fairly straightforward if you're using camelCase parameters in your request, but when presented with hyphen delimited values, how do you map these to an object?
Example for reference:
Controller:
#RestController
public class MyController {
#RequestMapping(value = "/search", method = RequestMethod.GET)
public ResponseEntity<String> search(RequestParams requestParams) {
return new ResponseEntity<>("my-val-1: " + requestParams.getMyVal1() + " my-val-2: " + requestParams.getMyVal2(), HttpStatus.OK);
}
}
Object to hold parameters:
public class RequestParams {
private String myVal1;
private String myVal2;
public RequestParams() {}
public String getMyVal1() {
return myVal1;
}
public void setMyVal1(String myVal1) {
this.myVal1 = myVal1;
}
public String getMyVal2() {
return myVal2;
}
public void setMyVal2(String myVal2) {
this.myVal2 = myVal2;
}
}
A request made like this works fine:
GET http://localhost:8080/search?myVal1=foo&myVal2=bar
But, what I want is for a request with hyphens to map to the object, like so:
GET http://localhost:8080/search?my-val-1=foo&my-val-2=bar
What do I need to configure in Spring to map url request parameters with hyphens to fields in an object? Bear in mind that we may have many parameters, so using a #RequestParam annotation for each field is not ideal.
I extended ServletRequestDataBinder and ServletModelAttributeMethodProcessor to solve the problem.
Consider that your domain object may already be annotated with #JsonProperty or #XmlElement for serialization. This example assumes this is the case. But you could also create your own custom annotation for this purpose e.g. #MyParamMapping.
An example of your annotated domain class is:
public class RequestParams {
#XmlElement(name = "my-val-1" )
#JsonProperty(value = "my-val-1")
private String myVal1;
#XmlElement(name = "my-val-2")
#JsonProperty(value = "my-val-2")
private String myVal2;
public RequestParams() {
}
public String getMyVal1() {
return myVal1;
}
public void setMyVal1(String myVal1) {
this.myVal1 = myVal1;
}
public String getMyVal2() {
return myVal2;
}
public void setMyVal2(String myVal2) {
this.myVal2 = myVal2;
}
}
You will need a SerletModelAttributeMethodProcessor to analyze the target class, generate a mapping, invoke your ServletRequestDataBinder.
public class KebabCaseProcessor extends ServletModelAttributeMethodProcessor {
public KebabCaseProcessor(boolean annotationNotRequired) {
super(annotationNotRequired);
}
#Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
private final Map<Class<?>, Map<String, String>> replaceMap = new ConcurrentHashMap<Class<?>, Map<String, String>>();
#Override
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest nativeWebRequest) {
Object target = binder.getTarget();
Class<?> targetClass = target.getClass();
if (!replaceMap.containsKey(targetClass)) {
Map<String, String> mapping = analyzeClass(targetClass);
replaceMap.put(targetClass, mapping);
}
Map<String, String> mapping = replaceMap.get(targetClass);
ServletRequestDataBinder kebabCaseDataBinder = new KebabCaseRequestDataBinder(target, binder.getObjectName(), mapping);
requestMappingHandlerAdapter.getWebBindingInitializer().initBinder(kebabCaseDataBinder, nativeWebRequest);
super.bindRequestParameters(kebabCaseDataBinder, nativeWebRequest);
}
private static Map<String, String> analyzeClass(Class<?> targetClass) {
Field[] fields = targetClass.getDeclaredFields();
Map<String, String> renameMap = new HashMap<String, String>();
for (Field field : fields) {
XmlElement xmlElementAnnotation = field.getAnnotation(XmlElement.class);
JsonProperty jsonPropertyAnnotation = field.getAnnotation(JsonProperty.class);
if (xmlElementAnnotation != null && !xmlElementAnnotation.name().isEmpty()) {
renameMap.put(xmlElementAnnotation.name(), field.getName());
} else if (jsonPropertyAnnotation != null && !jsonPropertyAnnotation.value().isEmpty()) {
renameMap.put(jsonPropertyAnnotation.value(), field.getName());
}
}
if (renameMap.isEmpty())
return Collections.emptyMap();
return renameMap;
}
}
This KebabCaseProcessor will use reflection to get a list of mappings for your request object. It will then invoke the KebabCaseDataBinder - passing in the mappings.
#Configuration
public class KebabCaseRequestDataBinder extends ExtendedServletRequestDataBinder {
private final Map<String, String> renameMapping;
public KebabCaseRequestDataBinder(Object target, String objectName, Map<String, String> mapping) {
super(target, objectName);
this.renameMapping = mapping;
}
protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {
super.addBindValues(mpvs, request);
for (Map.Entry<String, String> entry : renameMapping.entrySet()) {
String from = entry.getKey();
String to = entry.getValue();
if (mpvs.contains(from)) {
mpvs.add(to, mpvs.getPropertyValue(from).getValue());
}
}
}
}
All that remains now is to add this behavior to your configuration. The following configuration overrides the default configuration that the #EnableWebMVC delivers and adds this behavior to your request processing.
#Configuration
public static class WebContextConfiguration extends WebMvcConfigurationSupport {
#Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(kebabCaseProcessor());
}
#Bean
protected KebabCaseProcessor kebabCaseProcessor() {
return new KebabCaseProcessor(true);
}
}
Credit should be given to #Jkee. This solution is derivative of an example he posted here: How to customize parameter names when binding spring mvc command objects.
One way I can think of getting around the hyphens is to use HttpServletRequestWrapper class to wrap the original request.
Parse all the request parameters in this class and convert all hyphenated parameters into camelcase. After this, spring will be able to automatically map those parameters to your POJO classes.
public class CustomRequestWrapper extends HttpServletRequestWrapper {
private Map<String, String> camelCasedParams = new Hashmap();
public CustomRequestWrapper(HttpServletRequest req){
//Get all params from request.
//Transform each param name from hyphenated to camel case
//Put them in camelCasedParams;
}
public String getParameter(String name){
return camelCasedParams.get(name);
}
//Similarly, override other methods related to request parameters
}
Inject this request wrapper from J2EE filter. You can refer to below link for a tutorial on injecting request wrappers using filter.
http://www.programcreek.com/java-api-examples/javax.servlet.http.HttpServletRequestWrapper
Update your web xml to include filter and its filter mapping.

Jackson Mapper post-construct

I am using the Jackson ObjectMapper to deserialize some JSON into a Java class, which we'll call PlayerData. I would like to add a bit of logic to the PlayerData class to fix up some data after the fields have been loaded in. For example, some early JSON files used to use a "sex" flag instead of a "gender" falg, so if the sex flag is set but the gender flag is not set, I'd like to set the value of the gender field to be the value of the sex field.
Is there some sort of #PostConstruct or #AfterLoad annotation that I could affix to a method? Or perhaps an interface that I could implement? I didn't notice one in the documentation, but it seemed like an obvious feature.
Found this thru a link in the comments (credit: fedor.belov). This appears to allow you to run code post construct.
Adding a comment for people who end up here via
http://jira.codehaus.org/browse/JACKSON-645 or
http://jira.codehaus.org/browse/JACKSON-538 and are looking for a
method which is called after a deserializer completes. I was able to
achieve the desired effect by including an annotation and writing a
converter which uses the same class as input and output.
#JsonDeserialize(converter=MyClassSanitizer.class) // invoked after class is fully deserialized
public class MyClass {
public String field1;
}
import com.fasterxml.jackson.databind.util.StdConverter;
public class MyClassSanitizer extends StdConverter<MyClass,MyClass> {
#Override
public MyClass convert(MyClass var1) {
var1.field1 = munge(var1.field1);
return var1;
}
}
If you're not using the #JsonCreator, then Jackson will use the setter and getter methods to set the fields.
So if you define the following methods assuming that you have Sex and Gender enums:
#JsonProperty("sex")
public void setSex(final Sex sex) {
this.sex = sex;
if (gender == null) {
gender = (sex == Sex.WOMAN) ? Gender.WOMAN : Gender.MAN;
}
}
#JsonProperty("gender")
public void setGender(final Gender gender) {
this.gender = gender;
if (sex == null) {
sex = (gender == Gender.WOMAN) ? Sex.WOMAN : Sex.MAN;
}
}
it would work.
Update: You can find all of the annotations of Jackson library here.
Update2: Other solution:
class Example {
private final Sex sex;
private final Gender gender;
#JsonCreator
public Example(#JsonProperty("sex") final Sex sex) {
super();
this.sex = sex;
this.gender = getGenderBySex(sex)
}
#JsonFactory
public static Example createExample(#JsonProperty("gender") final Gender gender) {
return new Example(getSexByGender(gender));
}
private static Sex getSexByGender(final Gender) {
return (gender == Gender.WOMAN) ? Sex.WOMAN : Sex.MAN;
}
private static Gender getGenderBySex(final Sex) {
return (sex == Sex.WOMAN) ? Gender.WOMAN : Gender.MAN;
}
}
This is not supported out of the box, but you can easily create your #JsonPostDeserialize annotation for methods to be called after deserialization.
First, define the annotation:
/**
* Annotation for methods to be called directly after deserialization of the object.
*/
#Target({ ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
public #interface JsonPostDeserialize {
}
Then, add the following registration and implementation code to your project:
public static void addPostDeserializeSupport(ObjectMapper objectMapper) {
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier() {
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDescription,
JsonDeserializer<?> originalDeserializer) {
return new CustomAnnotationsDeserializer(originalDeserializer, beanDescription);
}
});
objectMapper.registerModule(module);
}
/**
* Class implementing the functionality of the {#link JsonPostDeserialize} annotation.
*/
public class CustomAnnotationsDeserializer extends DelegatingDeserializer {
private final BeanDescription beanDescription;
public CustomAnnotationsDeserializer(JsonDeserializer<?> delegate, BeanDescription beanDescription) {
super(delegate);
this.beanDescription = beanDescription;
}
#Override
protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee) {
return new CustomAnnotationsDeserializer(newDelegatee, beanDescription);
}
#Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
Object deserializedObject = super.deserialize(p, ctxt);
callPostDeserializeMethods(deserializedObject);
return deserializedObject;
}
private void callPostDeserializeMethods(Object deserializedObject) {
for (AnnotatedMethod method : beanDescription.getClassInfo().memberMethods()) {
if (method.hasAnnotation(JsonPostDeserialize.class)) {
try {
method.callOn(deserializedObject);
} catch (Exception e) {
throw new RuntimeException("Failed to call #JsonPostDeserialize annotated method in class "
+ beanDescription.getClassInfo().getName(), e);
}
}
}
}
}
Finally, modify your ObjectMapper instance with addPostDeserializeSupport, it will invoke all #JsonPostDeserialize annotated method of deserialized objects.
This is something that has actually been suggested couple of times earlier. So maybe filing an RFE would make sense; there are multiple ways in which this could work: obvious ones being ability to annotate type (#JsonPostProcess(Processor.class)) and ability to register post-processor through Module API (so that there's basically a callback when Jackson constructs deserializer, to let module specify post-processor to use if any). But perhaps there are even better ways to do this.

Categories