From 61d3a0bd1f8e51490dc40e7ff679a4e5b605c7b7 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 1 Jul 2021 08:50:24 +0200 Subject: [PATCH] Fix NPE when reading/mapping null value inside collection. Closes: #3686 --- .../core/convert/MappingMongoConverter.java | 3 +- .../core/convert/ReferenceLookupDelegate.java | 3 +- .../MappingMongoConverterUnitTests.java | 39 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index 732e8c9a5..aced009cd 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -1269,7 +1269,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App } for (Object element : source) { - items.add(context.convert(element, componentType)); + items.add(element != null ? context.convert(element, componentType) : element); } return getPotentiallyConvertedSimpleRead(items, targetType.getType()); @@ -2013,6 +2013,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App @SuppressWarnings("unchecked") public S convert(Object source, TypeInformation typeHint) { + Assert.notNull(source, "Source must not be null"); Assert.notNull(typeHint, "TypeInformation must not be null"); if (conversions.hasCustomReadTarget(source.getClass(), typeHint.getType())) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java index 616abb325..3ca730452 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java @@ -110,7 +110,8 @@ public final class ReferenceLookupDelegate { return null; } - return entityReader.read(result.iterator().next(), property.getTypeInformation()); + Object resultValue = result.iterator().next(); + return resultValue != null ? entityReader.read(resultValue, property.getTypeInformation()) : null; } private ReferenceCollection computeReferenceContext(MongoPersistentProperty property, Object value, diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java index 036157141..c8e2fec15 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java @@ -2532,6 +2532,41 @@ class MappingMongoConverterUnitTests { assertThat(document).containsEntry("writeAlwaysPerson", null).doesNotContainKey("writeNonNullPerson"); } + @Test // GH-3686 + void readsCollectionContainingNullValue() { + + org.bson.Document source = new org.bson.Document("items", Arrays.asList(new org.bson.Document("itemKey", "i1"), null, new org.bson.Document("itemKey", "i3"))); + + Order target = converter.read(Order.class, source); + + assertThat(target.items) + .map(it -> it != null ? it.itemKey : null) + .containsExactly("i1", null, "i3"); + } + + @Test // GH-3686 + void readsArrayContainingNullValue() { + + org.bson.Document source = new org.bson.Document("arrayOfStrings", Arrays.asList("i1", null, "i3")); + + WithArrays target = converter.read(WithArrays.class, source); + + assertThat(target.arrayOfStrings).containsExactly("i1", null, "i3"); + } + + @Test // GH-3686 + void readsMapContainingNullValue() { + + org.bson.Document source = new org.bson.Document("mapOfObjects", new org.bson.Document("item1", "i1").append("item2", null).append("item3", "i3")); + + ClassWithMapProperty target = converter.read(ClassWithMapProperty.class, source); + + assertThat(target.mapOfObjects) + .containsEntry("item1", "i1") + .containsEntry("item2", null) + .containsEntry("item3", "i3"); + } + static class GenericType { T content; } @@ -2893,6 +2928,10 @@ class MappingMongoConverterUnitTests { } + static class WithArrays { + String[] arrayOfStrings; + } + // DATAMONGO-1898 // DATACMNS-1278