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 33586de36..8cc823a0b 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 @@ -292,7 +292,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App Class rawType = typeToRead.getType(); if (conversions.hasCustomReadTarget(bson.getClass(), rawType)) { - return doConvert(bson, rawType); + return doConvert(bson, rawType, typeHint.getType()); } if (Document.class.isAssignableFrom(rawType)) { @@ -1532,9 +1532,17 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App return target; } + private T doConvert(Object value, Class target) { + return doConvert(value, target, null); + } + @SuppressWarnings("ConstantConditions") - private T doConvert(Object value, Class target) { - return conversionService.convert(value, target); + private T doConvert(Object value, Class target, @Nullable Class fallback) { + + if(conversionService.canConvert(value.getClass(), target) || fallback == null) { + return conversionService.convert(value, target); + } + return conversionService.convert(value, fallback); } /** 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 ffad28b23..db3a4e1b1 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 @@ -2383,6 +2383,51 @@ class MappingMongoConverterUnitTests { .doesNotContainKey("address.city"); } + @Test // GH-3580 + void shouldFallbackToConfiguredCustomConversionTargetOnRead() { + + GenericTypeConverter genericTypeConverter = spy(new GenericTypeConverter()); + + converter = new MappingMongoConverter(resolver, mappingContext); + converter.setCustomConversions(MongoCustomConversions.create(it -> { + it.registerConverter(genericTypeConverter); + })); + converter.afterPropertiesSet(); + + org.bson.Document source = new org.bson.Document("_class", SubTypeOfGenericType.class.getName()).append("value", + "v1"); + GenericType target = converter.read(GenericType.class, source); + + assertThat(target).isInstanceOf(GenericType.class); + assertThat(target.content).isEqualTo("v1"); + + verify(genericTypeConverter).convert(eq(source)); + } + + @Test // GH-3580 + void shouldUseMostConcreteCustomConversionTargetOnRead() { + + GenericTypeConverter genericTypeConverter = spy(new GenericTypeConverter()); + SubTypeOfGenericTypeConverter subTypeOfGenericTypeConverter = spy(new SubTypeOfGenericTypeConverter()); + + converter = new MappingMongoConverter(resolver, mappingContext); + converter.setCustomConversions(MongoCustomConversions.create(it -> { + it.registerConverter(genericTypeConverter); + it.registerConverter(subTypeOfGenericTypeConverter); + })); + converter.afterPropertiesSet(); + + org.bson.Document source = new org.bson.Document("_class", SubTypeOfGenericType.class.getName()).append("value", + "v1"); + GenericType target = converter.read(GenericType.class, source); + + assertThat(target).isInstanceOf(SubTypeOfGenericType.class); + assertThat(target.content).isEqualTo("v1_s"); + + verify(genericTypeConverter, never()).convert(any()); + verify(subTypeOfGenericTypeConverter).convert(eq(source)); + } + static class GenericType { T content; } @@ -2899,4 +2944,31 @@ class MappingMongoConverterUnitTests { } } + static class SubTypeOfGenericType extends GenericType { + + } + + @ReadingConverter + static class GenericTypeConverter implements Converter> { + + @Override + public GenericType convert(org.bson.Document source) { + + GenericType target = new GenericType<>(); + target.content = source.get("value"); + return target; + } + } + + @ReadingConverter + static class SubTypeOfGenericTypeConverter implements Converter { + + @Override + public SubTypeOfGenericType convert(org.bson.Document source) { + + SubTypeOfGenericType target = new SubTypeOfGenericType(); + target.content = source.getString("value") + "_s"; + return target; + } + } }