diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 9202c22415..a5ee5a660a 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -984,6 +984,8 @@ João Guerra (joca-bt@github) * Reported #2473: Array index missing in path of `JsonMappingException` for `Collection`, with custom deserializer (2.10.1) + * Reported #2567: Incorrect target type for arrays when providing nulls and nulls are disabled + (2.10.2) Ryan Bohn (bohnman@github) * Reported #2475: `StringCollectionSerializer` calls `JsonGenerator.setCurrentValue(value)`, diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 2c913841e7..2d15899f31 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -14,6 +14,8 @@ Project: jackson-databind (reported by Fabian L) #2560: Check `WRAP_EXCEPTIONS` in `CollectionDeserializer.handleNonArray()` (reported by Stefan W) +#2567: Incorrect target type for arrays when providing nulls and nulls are disabled + (reported by João G) 2.10.1 (09-Nov-2019) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsFailProvider.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsFailProvider.java index ba870c7da1..7c6f9bad7a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsFailProvider.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsFailProvider.java @@ -23,7 +23,12 @@ protected NullsFailProvider(PropertyName name, JavaType type) { } public static NullsFailProvider constructForProperty(BeanProperty prop) { - return new NullsFailProvider(prop.getFullName(), prop.getType()); + return constructForProperty(prop, prop.getType()); + } + + // @since 2.10.2 + public static NullsFailProvider constructForProperty(BeanProperty prop, JavaType type) { + return new NullsFailProvider(prop.getFullName(), type); } public static NullsFailProvider constructForRootValue(JavaType t) { diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java index 65abbeafd8..78a809571d 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java @@ -110,9 +110,11 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, nuller = NullsConstantProvider.skipper(); } else if (nullStyle == Nulls.FAIL) { if (property == null) { - nuller = NullsFailProvider.constructForRootValue(ctxt.constructType(_valueClass)); + // 09-Dec-2019, tatu: [databind#2567] need to ensure correct target type + nuller = NullsFailProvider.constructForRootValue(ctxt.constructType(_valueClass.getComponentType())); } else { - nuller = NullsFailProvider.constructForProperty(property); + // 09-Dec-2019, tatu: [databind#2567] need to ensure correct target type + nuller = NullsFailProvider.constructForProperty(property, property.getType().getContentType()); } } if ((unwrapSingle == _unwrapSingle) && (nuller == _nuller)) { @@ -198,29 +200,7 @@ public T deserialize(JsonParser p, DeserializationContext ctxt, T existing) thro /* Helper methods for sub-classes /******************************************************** */ - - /* - * Convenience method that constructs a concatenation of two arrays, - * with the type they have. - * - * @since 2.9 - @SuppressWarnings("unchecked") - public static T concatArrays(T array1, T array2) - { - int len1 = Array.getLength(array1); - if (len1 == 0) { - return array2; - } - int len2 = Array.getLength(array2); - if (len2 == 0) { - return array1; - } - Object result = Arrays.copyOf((Object[]) array1, len1 + len2); - System.arraycopy(array2, 0, result, len1, len2); - return (T) result; - } - */ - + @SuppressWarnings("unchecked") protected T handleNonArray(JsonParser p, DeserializationContext ctxt) throws IOException { diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java index c94d4fd4d8..dc4f3aef96 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java @@ -1098,6 +1098,20 @@ protected NullValueProvider findContentNullProvider(DeserializationContext ctxt, if (nulls == Nulls.SKIP) { return NullsConstantProvider.skipper(); } + // 09-Dec-2019, tatu: [databind#2567] need to ensure correct target type (element, + // not container), so inlined here before calling _findNullProvider + if (nulls == Nulls.FAIL) { + if (prop == null) { + JavaType type = ctxt.constructType(valueDeser.handledType()); + // should always be container? But let's double-check just in case: + if (type.isContainerType()) { + type = type.getContentType(); + } + return NullsFailProvider.constructForRootValue(type); + } + return NullsFailProvider.constructForProperty(prop, prop.getType().getContentType()); + } + NullValueProvider prov = _findNullProvider(ctxt, prop, nulls, valueDeser); if (prov != null) { return prov; diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsForContentTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsForContentTest.java index e5cd9d1ae5..66350c0b4e 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsForContentTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/filter/NullConversionsForContentTest.java @@ -62,6 +62,7 @@ public void testFailOnNullFromDefaults() throws Exception fail("Should not pass"); } catch (InvalidNullException e) { verifyException(e, "property \"values\""); + assertEquals(String.class, e.getTargetType()); } // or configured for type: @@ -73,6 +74,7 @@ public void testFailOnNullFromDefaults() throws Exception fail("Should not pass"); } catch (InvalidNullException e) { verifyException(e, "property \"values\""); + assertEquals(String.class, e.getTargetType()); } } @@ -96,6 +98,7 @@ public void testFailOnNullWithCollections() throws Exception fail("Should not pass"); } catch (InvalidNullException e) { verifyException(e, "property \"noNulls\""); + assertEquals(Integer.class, e.getTargetType()); } // List @@ -104,6 +107,7 @@ public void testFailOnNullWithCollections() throws Exception fail("Should not pass"); } catch (InvalidNullException e) { verifyException(e, "property \"noNulls\""); + assertEquals(String.class, e.getTargetType()); } } @@ -116,6 +120,7 @@ public void testFailOnNullWithArrays() throws Exception fail("Should not pass"); } catch (InvalidNullException e) { verifyException(e, "property \"noNulls\""); + assertEquals(Object.class, e.getTargetType()); } // String[] @@ -124,6 +129,7 @@ public void testFailOnNullWithArrays() throws Exception fail("Should not pass"); } catch (InvalidNullException e) { verifyException(e, "property \"noNulls\""); + assertEquals(String.class, e.getTargetType()); } } @@ -137,6 +143,7 @@ public void testFailOnNullWithPrimitiveArrays() throws Exception fail("Should not pass"); } catch (InvalidNullException e) { verifyException(e, "property \"noNulls\""); + assertEquals(Boolean.TYPE, e.getTargetType()); } // int[] try { @@ -144,6 +151,7 @@ public void testFailOnNullWithPrimitiveArrays() throws Exception fail("Should not pass"); } catch (InvalidNullException e) { verifyException(e, "property \"noNulls\""); + assertEquals(Integer.TYPE, e.getTargetType()); } // double[] try { @@ -151,6 +159,7 @@ public void testFailOnNullWithPrimitiveArrays() throws Exception fail("Should not pass"); } catch (InvalidNullException e) { verifyException(e, "property \"noNulls\""); + assertEquals(Double.TYPE, e.getTargetType()); } } @@ -163,6 +172,7 @@ public void testFailOnNullWithMaps() throws Exception fail("Should not pass"); } catch (InvalidNullException e) { verifyException(e, "property \"noNulls\""); + assertEquals(String.class, e.getTargetType()); } // Then: EnumMap @@ -172,6 +182,7 @@ public void testFailOnNullWithMaps() throws Exception fail("Should not pass"); } catch (InvalidNullException e) { verifyException(e, "property \"noNulls\""); + assertEquals(String.class, e.getTargetType()); } }