2

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>.

mucaho
  • 2,119
  • 20
  • 35
  • Do you mean for JSON like `{"someArray": null}` or `{"someObject": null"}`? What about the lack of an entry? – Sotirios Delimanolis Sep 05 '14 at 19:15
  • Yes, the deserialization should fail when it encounters `{"someArray": null}` or `{"someObject": null}`. Jackson has already the option to fail on omitted properties - [Deserialization Feature - FAIL_ON_IGNORED_PROPERTIES](https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features#failure-handling). – mucaho Sep 05 '14 at 19:50
  • No, I meant how would you handle `{}` as opposed to `{"someArray":null}`? – Sotirios Delimanolis Sep 05 '14 at 19:54
  • I do not understand your question. Could you try to elaborate? My motivation behind this was to get an exception when deserializing JSON with `null` properties to custom POJOs. – mucaho Sep 05 '14 at 20:28
  • `{}` should deserialize just fine into an empty POJO `class MyClass {}`. `{"someArray": null}` should throw an exception when deserializing into a POJO `class MyClass { private List someArray; //+setter/getter }` – mucaho Sep 05 '14 at 20:35
  • What about `{}` into that second POJO class? – Sotirios Delimanolis Sep 05 '14 at 20:41
  • Deserializing `{}` into the 2nd POJO class should throw no exception on its own (the field should be initialized to `null` per java standard). However, with [Deserialization Feature - FAIL_ON_IGNORED_PROPERTIES](https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features#failure-handling) enabled, it should throw an exception. – mucaho Sep 05 '14 at 20:49
  • 1
    Ok, now that we have the full idea, why do you want to differentiate between `{}` and `{"a":null}`? In my opinion they should both deserialize to the same. (`FAIL_ON_IGNORED_PROPERTIES` will not affect this use case. That works with `JsonIgnoreProperties` and its like.) – Sotirios Delimanolis Sep 05 '14 at 20:59
  • `{}` says "use the default field value of the class". `{"a": null}` says "overwrite the default field value (possibly a reference to an object) of the class with `null`". It's a slight, but noticeable difference. Ah thanks for enlightening me about `FAIL_ON_IGNORED_PROPERTIES`; means one more case to cover then :(. – mucaho Sep 05 '14 at 21:08
  • I see now. There seems to have been validation against `null` in Jackson 1, but it has since been removed in Jackson 2. Good luck, you seem to have a nice working solution. – Sotirios Delimanolis Sep 05 '14 at 21:32
  • 1
    Yeah, thanks. On the other hand I realized now I should use the `@JsonCreator` annotation, which would allow me to do more serious validation inside e.g. a factory method. I was at first repelled to use annotations (specifically in the case of non-editable code), but I found out about the [Mixin Annotations](http://wiki.fasterxml.com/JacksonMixInAnnotations) which allow you to add annotations even to non-editable code. – mucaho Sep 05 '14 at 21:53

1 Answers1

1

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);
    }
}
Community
  • 1
  • 1
mucaho
  • 2,119
  • 20
  • 35