Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson Dynamic filtering of properties during deserialization

I have a REST WS to update a bean object which receives a JSON string as input.

ABean entity = svc.findEntity(...);
objectMapper.readerForUpdating(entity).readValue(json);
[...]
svc.save(entity);

ABean is a complex type containing also other objects e.g.:

class ABean {
    public BBean b;
    public CBean c;

    public String d;
}

svc.save(...) will save the bean and the embedded objects.

For security reasons I want to filter out some of the properties that can be updated by the JSON string, but I want to do this dynamically, so that for every WS (or user Role) I can decide which properties to prevent to be updated (so I can't simply use the Jackson Views)

To summarize, is there any way I can dynamically filter out properties during JSON Deserialization?

like image 976
Mario Avatar asked Sep 06 '12 17:09

Mario


People also ask

How do I ignore properties in ObjectMapper?

ObjectMapper; ObjectMapper objectMapper = new ObjectMapper(); objectMapper. configure(DeserializationFeature. FAIL_ON_UNKNOWN_PROPERTIES, false); This will now ignore unknown properties for any JSON it's going to parse, You should only use this option if you can't annotate a class with @JsonIgnoreProperties annotation.

What is the use of Jackson ObjectMapper?

ObjectMapper is the main actor class of Jackson library. ObjectMapper class ObjectMapper provides functionality for reading and writing JSON, either to and from basic POJOs (Plain Old Java Objects), or to and from a general-purpose JSON Tree Model (JsonNode), as well as related functionality for performing conversions.

What does ObjectMapper readValue do?

The simple readValue API of the ObjectMapper is a good entry point. We can use it to parse or deserialize JSON content into a Java object. Also, on the writing side, we can use the writeValue API to serialize any Java object as JSON output.

Does Jackson ObjectMapper use reflection?

Without any annotations, the Jackson ObjectMapper uses reflection to do the POJO mapping. Because of the reflection, it works on all fields regardless of the access modifier.


2 Answers

Another way is using BeanDeserializerModifier:

private static class BeanDeserializerModifierForIgnorables extends BeanDeserializerModifier {

        private java.lang.Class<?> type;
        private List<String> ignorables;

        public BeanDeserializerModifierForIgnorables(java.lang.Class clazz, String... properties) {
            ignorables = new ArrayList<>();
            for(String property : properties) {
                ignorables.add(property);
            }
            this.type = clazz;
        }

        @Override
        public BeanDeserializerBuilder updateBuilder(
                DeserializationConfig config, BeanDescription beanDesc,
                BeanDeserializerBuilder builder) {
            if(!type.equals(beanDesc.getBeanClass())) {
                return builder;
            }

            for(String ignorable : ignorables) {
                builder.addIgnorable(ignorable);                
            }

            return builder;
        }

        @Override
        public List<BeanPropertyDefinition> updateProperties(
                DeserializationConfig config, BeanDescription beanDesc,
                List<BeanPropertyDefinition> propDefs) {
            if(!type.equals(beanDesc.getBeanClass())) {
                return propDefs;
            }

            List<BeanPropertyDefinition> newPropDefs = new ArrayList<>();
            for(BeanPropertyDefinition propDef : propDefs) {
                if(!ignorables.contains(propDef.getName())) {
                    newPropDefs.add(propDef);
                }
            }
            return newPropDefs;
        }
    }

You can register the modfier to the ObjectMapper with:

BeanDeserializerModifier modifier = new BeanDeserializerModifierForIgnorables(YourType.class, "name");
DeserializerFactory dFactory = BeanDeserializerFactory.instance.withDeserializerModifier(modifier);
ObjectMapper mapper = new ObjectMapper(null, null, new DefaultDeserializationContext.Impl(dFactory));

Then the defined properties are ignored. You can ignore the updateBuilder method if you use the @JsonAnySetter annotation.

Greetings, Martin

like image 112
Martin Avatar answered Oct 23 '22 02:10

Martin


I assume from your description that you can't simply use the @JsonIgnore annotation to prevent the fields from being serialized for each particular class.

Look at using Jakson mix-ins: mix-ins allow you to substitute a class definition - with the necessary annotations - for data binding. During serialization you can choose a particular mix-in class definition to stand in for the actual class being serialized. Define a set of mix-ins to handle every case, then choose which is appropriate when you serialize a particular bean.

like image 22
pb2q Avatar answered Oct 23 '22 02:10

pb2q