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 e0f5b53ea..af84e780c 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 @@ -17,7 +17,16 @@ package org.springframework.data.mongodb.core.convert; import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -30,6 +39,7 @@ import org.bson.codecs.configuration.CodecRegistry; import org.bson.conversions.Bson; import org.bson.json.JsonReader; import org.bson.types.ObjectId; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.context.ApplicationContext; @@ -395,8 +405,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App EntityProjection property = returnedTypeDescriptor.findProperty(name); if (property == null) { - return new ConversionContext(sourceConverter, conversions, path, MappingMongoConverter.this::readDocument, collectionConverter, - mapConverter, dbRefConverter, elementConverter); + return new ConversionContext(sourceConverter, conversions, path, MappingMongoConverter.this::readDocument, + collectionConverter, mapConverter, dbRefConverter, elementConverter); } return new ProjectingConversionContext(sourceConverter, conversions, path, collectionConverter, mapConverter, @@ -938,9 +948,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App TypeInformation type = prop.getTypeInformation(); if (conversions.hasValueConverter(prop)) { - accessor.put(prop, - conversions.getPropertyValueConversions().getValueConverter(prop) - .write(obj, new MongoConversionContext(prop, this))); + accessor.put(prop, conversions.getPropertyValueConversions().getValueConverter(prop).write(obj, + new MongoConversionContext(prop, this))); return; } @@ -1275,9 +1284,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App DocumentAccessor accessor = new DocumentAccessor(bson); if (conversions.hasValueConverter(property)) { - accessor.put(property, - conversions.getPropertyValueConversions().getValueConverter(property) - .write(value, new MongoConversionContext(property, this))); + accessor.put(property, conversions.getPropertyValueConversions().getValueConverter(property).write(value, + new MongoConversionContext(property, this))); return; } @@ -1927,12 +1935,15 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App return null; } - if (context.conversions.hasValueConverter(property)) { - return (T) context.conversions.getPropertyValueConversions().getValueConverter(property).read(value, + CustomConversions conversions = context.conversions; + if (conversions.hasValueConverter(property)) { + return (T) conversions.getPropertyValueConversions().getValueConverter(property).read(value, new MongoConversionContext(property, context.sourceConverter)); } - return (T) context.convert(value, property.getTypeInformation()); + ConversionContext contextToUse = context.forProperty(property.getName()); + + return (T) contextToUse.convert(value, property.getTypeInformation()); } public MongoDbPropertyValueProvider withContext(ConversionContext context) { 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 e6b5c8db7..44f6d700d 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 @@ -45,6 +45,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; + import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.ConversionNotSupportedException; import org.springframework.beans.factory.annotation.Autowired; @@ -2707,6 +2708,23 @@ class MappingMongoConverterUnitTests { assertThat(projection.getName()).isEqualTo("my-book by Walter White"); } + @Test // GH-4120 + void shouldReadDtoProjection() { + + org.bson.Document author = new org.bson.Document("firstName", "Walter").append("lastName", "White"); + org.bson.Document book = new org.bson.Document("_id", "foo").append("name", "my-book").append("author", author); + + EntityProjectionIntrospector introspector = EntityProjectionIntrospector.create(converter.getProjectionFactory(), + EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy() + .and((target, underlyingType) -> !converter.conversions.isSimpleType(target)), + mappingContext); + + AuthorOnly projection = converter.project(introspector.introspect(AuthorOnly.class, Book.class), book); + + assertThat(projection.getAuthor().getFirstName()).isEqualTo("Walter"); + assertThat(projection.getAuthor().getLastName()).isEqualTo("White"); + } + @Test // GH-3596 void simpleConverter() { @@ -3610,6 +3628,21 @@ class MappingMongoConverterUnitTests { String getName(); } + @lombok.Value + static class AuthorOnly { + + AuthorNameOnly author; + } + + @lombok.Value + static class AuthorNameOnly { + + String firstName; + + String lastName; + + } + @Data static class Book {