mucaho
mucaho

Reputation: 2169

Jackson - Disallow null for object-properties and array-elements during deserialization

Is there an option in Jackson to let the deserialization fail when it encounters a null for any (non-primitive) object property or for any (non-primitive) array element?

It should work similarly to the Deserialization Feature - FAIL_ON_NULL_FOR_PRIMITIVES).

Example #1

So deserializing {"name": null} should throw an exception when deserializing into a POJO

class User { 
    private String name = "Default name";
    //+setters/getters 
}

However, it should work fine and throw no exception when deserializing {} into that POJO, as the default value for the name field is not overwritten (see comments).

Example #2

I would like to avoid null elements in arrays also, so deserializing ["A", "B", null] should throw an exception when deserializing into List<String>.

Upvotes: 2

Views: 2478

Answers (1)

mucaho
mucaho

Reputation: 2169

There is no easy way to do this as far as I know (jackson-databind 2.4.2).
I suggest you take a look at using custom constructors / factory methods for creating objects out of Json. That allows you to do more advanced validation of incoming Json strings.

Solution to example #1

You can add this feature by registering a SimpleModule with an added BeanDeserializerModifier in order to alter the deserialization functionality.
By overriding the appropriate method you can use the default JsonDeserializer to deserialize the object easily and throw a mapping exception if a null property occurs.
You can find details in the answers of a similar SO question.

Extend the existing deserialization:

//instantiate an objectMapper and alter the deserialization functionality
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.setDeserializerModifier(new BeanDeserializerModifier() {
    @Override
    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
        return new DisallowNullDeserializer(beanDesc.getBeanClass(), deserializer);
    }
});
mapper.registerModule(simpleModule);

The actual deserialization and exception throwing is happening in this utility class:

public class DisallowNullDeserializer<T> extends StdDeserializer<T> implements ResolvableDeserializer {
    private final JsonDeserializer<?> delegateDeserializer;

    public DisallowNullDeserializer(Class<T> clazz, JsonDeserializer<?> delegateDeserializer) {
        super(clazz);
        this.delegateDeserializer = delegateDeserializer;
    }

    @Override
    public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        // delegate actual deserialization to default deserializer
        T out = (T) delegateDeserializer.deserialize(jp, ctxt);

        // check for null properties & throw exception
        // -> there may be a better, more performant way to find null properties
        Map<String, Object> propertyMap = mapper.convertValue(out, Map.class);
        for (Object property: propertyMap.values())
            if (property == null)
                throw ctxt.mappingException("Can not map JSON null values.");

        return out;
    }

    // there is no obvious reason why this is needed; see linked SO answers 
    @Override
    public void resolve(DeserializationContext ctxt) throws JsonMappingException {
        ((ResolvableDeserializer) delegateDeserializer).resolve(ctxt);
    }
}

Upvotes: 1

Related Questions