Skip to content

Commit ece261a

Browse files
christophstroblmp911de
authored andcommitted
Fix conversion for types having a converter registered.
Fixes: #3660 Original pull request: #3662.
1 parent dae0ac3 commit ece261a

File tree

2 files changed

+206
-4
lines changed

2 files changed

+206
-4
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ protected ConversionContext getConversionContext(ObjectPath path) {
171171

172172
Assert.notNull(path, "ObjectPath must not be null");
173173

174-
return new ConversionContext(path, this::readDocument, this::readCollectionOrArray, this::readMap, this::readDBRef,
174+
return new ConversionContext(conversions, path, this::readDocument, this::readCollectionOrArray, this::readMap, this::readDBRef,
175175
this::getPotentiallyConvertedSimpleRead);
176176
}
177177

@@ -1323,7 +1323,7 @@ protected Map<Object, Object> readMap(ConversionContext context, Bson bson, Type
13231323
}
13241324

13251325
Object value = entry.getValue();
1326-
map.put(key, context.convert(value, valueType));
1326+
map.put(key, value == null ? value : context.convert(value, valueType));
13271327
}
13281328

13291329
return map;
@@ -1970,17 +1970,19 @@ public org.springframework.data.util.TypeInformation<? extends S> specialize(Cla
19701970
*/
19711971
protected static class ConversionContext {
19721972

1973+
private final org.springframework.data.convert.CustomConversions conversions;
19731974
private final ObjectPath path;
19741975
private final ContainerValueConverter<Bson> documentConverter;
19751976
private final ContainerValueConverter<Collection<?>> collectionConverter;
19761977
private final ContainerValueConverter<Bson> mapConverter;
19771978
private final ContainerValueConverter<DBRef> dbRefConverter;
19781979
private final ValueConverter<Object> elementConverter;
19791980

1980-
ConversionContext(ObjectPath path, ContainerValueConverter<Bson> documentConverter,
1981+
ConversionContext(org.springframework.data.convert.CustomConversions customConversions, ObjectPath path, ContainerValueConverter<Bson> documentConverter,
19811982
ContainerValueConverter<Collection<?>> collectionConverter, ContainerValueConverter<Bson> mapConverter,
19821983
ContainerValueConverter<DBRef> dbRefConverter, ValueConverter<Object> elementConverter) {
19831984

1985+
this.conversions = customConversions;
19841986
this.path = path;
19851987
this.documentConverter = documentConverter;
19861988
this.collectionConverter = collectionConverter;
@@ -2001,6 +2003,10 @@ public <S extends Object> S convert(Object source, TypeInformation<? extends S>
20012003

20022004
Assert.notNull(typeHint, "TypeInformation must not be null");
20032005

2006+
if (conversions.hasCustomReadTarget(source.getClass(), typeHint.getType())) {
2007+
return (S) elementConverter.convert(source, typeHint);
2008+
}
2009+
20042010
if (source instanceof Collection) {
20052011

20062012
Class<?> rawType = typeHint.getType();
@@ -2046,7 +2052,7 @@ public ConversionContext withPath(ObjectPath currentPath) {
20462052

20472053
Assert.notNull(currentPath, "ObjectPath must not be null");
20482054

2049-
return new ConversionContext(currentPath, documentConverter, collectionConverter, mapConverter, dbRefConverter,
2055+
return new ConversionContext(conversions, currentPath, documentConverter, collectionConverter, mapConverter, dbRefConverter,
20502056
elementConverter);
20512057
}
20522058

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java

+196
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@
8181
import org.springframework.data.mongodb.core.mapping.Unwrapped;
8282
import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback;
8383
import org.springframework.data.util.ClassTypeInformation;
84+
import org.springframework.lang.NonNull;
85+
import org.springframework.lang.Nullable;
8486
import org.springframework.test.util.ReflectionTestUtils;
8587

8688
import com.mongodb.BasicDBList;
@@ -2428,6 +2430,98 @@ void shouldUseMostConcreteCustomConversionTargetOnRead() {
24282430
verify(subTypeOfGenericTypeConverter).convert(eq(source));
24292431
}
24302432

2433+
2434+
@Test // GH-3660
2435+
void usesCustomConverterForMapTypesOnWrite() {
2436+
2437+
2438+
converter = new MappingMongoConverter(resolver, mappingContext);
2439+
converter.setCustomConversions(MongoCustomConversions.create(it -> {
2440+
it.registerConverter(new TypeImplementingMapToDocumentConverter());
2441+
}));
2442+
converter.afterPropertiesSet();
2443+
2444+
TypeImplementingMap source = new TypeImplementingMap("one", 2);
2445+
org.bson.Document target = new org.bson.Document();
2446+
2447+
converter.write(source, target);
2448+
2449+
assertThat(target).containsEntry("1st", "one").containsEntry("2nd", 2);
2450+
}
2451+
2452+
@Test // GH-3660
2453+
void usesCustomConverterForTypesImplementingMapOnWrite() {
2454+
2455+
converter = new MappingMongoConverter(resolver, mappingContext);
2456+
converter.setCustomConversions(MongoCustomConversions.create(it -> {
2457+
it.registerConverter(new TypeImplementingMapToDocumentConverter());
2458+
}));
2459+
converter.afterPropertiesSet();
2460+
2461+
TypeImplementingMap source = new TypeImplementingMap("one", 2);
2462+
org.bson.Document target = new org.bson.Document();
2463+
2464+
converter.write(source, target);
2465+
2466+
assertThat(target).containsEntry("1st", "one").containsEntry("2nd", 2);
2467+
}
2468+
2469+
@Test // GH-3660
2470+
void usesCustomConverterForTypesImplementingMapOnRead() {
2471+
2472+
converter = new MappingMongoConverter(resolver, mappingContext);
2473+
converter.setCustomConversions(MongoCustomConversions.create(it -> {
2474+
it.registerConverter(new DocumentToTypeImplementingMapConverter());
2475+
}));
2476+
converter.afterPropertiesSet();
2477+
2478+
org.bson.Document source = new org.bson.Document("1st", "one")
2479+
.append("2nd", 2)
2480+
.append("_class", TypeImplementingMap.class.getName());
2481+
2482+
TypeImplementingMap target = converter.read(TypeImplementingMap.class, source);
2483+
2484+
assertThat(target).isEqualTo(new TypeImplementingMap("one", 2));
2485+
}
2486+
2487+
@Test // GH-3660
2488+
void usesCustomConverterForPropertiesUsingTypesThatImplementMapOnWrite() {
2489+
2490+
converter = new MappingMongoConverter(resolver, mappingContext);
2491+
converter.setCustomConversions(MongoCustomConversions.create(it -> {
2492+
it.registerConverter(new TypeImplementingMapToDocumentConverter());
2493+
}));
2494+
converter.afterPropertiesSet();
2495+
2496+
TypeWrappingTypeImplementingMap source = new TypeWrappingTypeImplementingMap();
2497+
source.typeImplementingMap = new TypeImplementingMap("one", 2);
2498+
org.bson.Document target = new org.bson.Document();
2499+
2500+
converter.write(source, target);
2501+
2502+
assertThat(target).containsEntry("typeImplementingMap", new org.bson.Document("1st", "one").append("2nd", 2));
2503+
}
2504+
2505+
@Test // GH-3660
2506+
void usesCustomConverterForPropertiesUsingTypesImplementingMapOnRead() {
2507+
2508+
converter = new MappingMongoConverter(resolver, mappingContext);
2509+
converter.setCustomConversions(MongoCustomConversions.create(it -> {
2510+
it.registerConverter(new DocumentToTypeImplementingMapConverter());
2511+
}));
2512+
converter.afterPropertiesSet();
2513+
2514+
org.bson.Document source = new org.bson.Document("typeImplementingMap",
2515+
new org.bson.Document("1st", "one")
2516+
.append("2nd", 2))
2517+
.append("_class", TypeWrappingTypeImplementingMap.class.getName());
2518+
2519+
TypeWrappingTypeImplementingMap target = converter.read(TypeWrappingTypeImplementingMap.class, source);
2520+
2521+
assertThat(target.typeImplementingMap).isEqualTo(new TypeImplementingMap("one", 2));
2522+
}
2523+
2524+
24312525
static class GenericType<T> {
24322526
T content;
24332527
}
@@ -2971,4 +3065,106 @@ public SubTypeOfGenericType convert(org.bson.Document source) {
29713065
return target;
29723066
}
29733067
}
3068+
3069+
@WritingConverter
3070+
static class TypeImplementingMapToDocumentConverter implements Converter<TypeImplementingMap, org.bson.Document> {
3071+
3072+
@Nullable
3073+
@Override
3074+
public org.bson.Document convert(TypeImplementingMap source) {
3075+
return new org.bson.Document("1st", source.val1).append("2nd", source.val2);
3076+
}
3077+
}
3078+
3079+
@ReadingConverter
3080+
static class DocumentToTypeImplementingMapConverter implements Converter<org.bson.Document, TypeImplementingMap> {
3081+
3082+
@Nullable
3083+
@Override
3084+
public TypeImplementingMap convert(org.bson.Document source) {
3085+
return new TypeImplementingMap(source.getString("1st"), source.getInteger("2nd"));
3086+
}
3087+
}
3088+
3089+
static class TypeWrappingTypeImplementingMap {
3090+
3091+
String id;
3092+
TypeImplementingMap typeImplementingMap;
3093+
}
3094+
3095+
@EqualsAndHashCode
3096+
static class TypeImplementingMap implements Map<String,String> {
3097+
3098+
String val1;
3099+
int val2;
3100+
3101+
public TypeImplementingMap(String val1, int val2) {
3102+
this.val1 = val1;
3103+
this.val2 = val2;
3104+
}
3105+
3106+
@Override
3107+
public int size() {
3108+
return 0;
3109+
}
3110+
3111+
@Override
3112+
public boolean isEmpty() {
3113+
return false;
3114+
}
3115+
3116+
@Override
3117+
public boolean containsKey(Object key) {
3118+
return false;
3119+
}
3120+
3121+
@Override
3122+
public boolean containsValue(Object value) {
3123+
return false;
3124+
}
3125+
3126+
@Override
3127+
public String get(Object key) {
3128+
return null;
3129+
}
3130+
3131+
@Nullable
3132+
@Override
3133+
public String put(String key, String value) {
3134+
return null;
3135+
}
3136+
3137+
@Override
3138+
public String remove(Object key) {
3139+
return null;
3140+
}
3141+
3142+
@Override
3143+
public void putAll(@NonNull Map<? extends String, ? extends String> m) {
3144+
3145+
}
3146+
3147+
@Override
3148+
public void clear() {
3149+
3150+
}
3151+
3152+
@NonNull
3153+
@Override
3154+
public Set<String> keySet() {
3155+
return null;
3156+
}
3157+
3158+
@NonNull
3159+
@Override
3160+
public Collection<String> values() {
3161+
return null;
3162+
}
3163+
3164+
@NonNull
3165+
@Override
3166+
public Set<Entry<String, String>> entrySet() {
3167+
return null;
3168+
}
3169+
}
29743170
}

0 commit comments

Comments
 (0)