diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java index da74024f4a..53bf41faf6 100644 --- a/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java +++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java @@ -230,7 +230,20 @@ public enum DeserializationFeature implements ConfigFeature * @since 2.6 */ FAIL_ON_MISSING_CREATOR_PROPERTIES(false), - + + /** + * Feature that determines what happens if one or more Creator properties (properties + * bound to parameters of Creator method (constructor or static factory method)) + * are bound to null values - either from the JSON or as a default value. This + * is useful if you want to avoid nulls in your codebase, and particularly useful + * if you are using Java or Scala optionals for non-mandatory fields. + * Feature is disabled by default, so that no exception is thrown for missing creator + * property values, unless they are explicitly marked as `required`. + * + * @since 2.8 + */ + FAIL_ON_NULL_CREATOR_PROPERTIES(false), + /** * Feature that determines whether Jackson code should catch * and wrap {@link Exception}s (but never {@link Error}s!) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java index 32e100b7e0..a2b35a9086 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java @@ -125,6 +125,16 @@ protected Object[] getParameters(SettableBeanProperty[] props) } } } + + if (_context.isEnabled(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES)) { + for (int ix = 0; ix < props.length; ++ix) { + if (_creatorParameters[ix] == null) { + throw _context.mappingException("Null value for creator property '%s'; DeserializationFeature.FAIL_ON_NULL_FOR_CREATOR_PARAMETERS enabled", + props[ix].getName(), props[ix].getCreatorIndex()); + } + } + } + return _creatorParameters; } diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/FailOnNullCreatorTest.java b/src/test/java/com/fasterxml/jackson/databind/creators/FailOnNullCreatorTest.java new file mode 100644 index 0000000000..52416f4f47 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/creators/FailOnNullCreatorTest.java @@ -0,0 +1,60 @@ +package com.fasterxml.jackson.databind.creators; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.*; + +/** + * Tests to ensure that deserialization fails when a bean property has a null value + * Relates to issue #988 + */ +public class FailOnNullCreatorTest extends BaseMapTest +{ + static class Person { + String name; + Integer age; + + @JsonCreator + public Person(@JsonProperty(value="name") String name, + @JsonProperty(value="age") int age) + { + this.name = name; + this.age = age; + } + } + + private final ObjectReader POINT_READER = objectMapper().readerFor(Person.class); + + public void testRequiredNonNullParam() throws Exception + { + Person p; + // First: fine if feature is not enabled + p = POINT_READER.readValue(aposToQuotes("{}")); + assertEquals(null, p.name); + assertEquals(Integer.valueOf(0), p.age); + + // Second: fine if feature is enabled but default value is not null + ObjectReader r = POINT_READER.with(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES); + p = POINT_READER.readValue(aposToQuotes("{'name':'John', 'age': null}")); + assertEquals("John", p.name); + assertEquals(Integer.valueOf(0), p.age); + + // Third: throws exception if property is missing + try { + r.readValue(aposToQuotes("{}")); + fail("Should not pass third test"); + } catch (JsonMappingException e) { + verifyException(e, "Null value for creator property 'name'"); + } + + // Fourth: throws exception if property is set to null explicitly + try { + r.readValue(aposToQuotes("{'age': 5, 'name': null}")); + fail("Should not pass fourth test"); + } catch (JsonMappingException e) { + verifyException(e, "Null value for creator property 'name'"); + } + } + + +}