From d8bb644b30bb1db35e6df20b217d929e52695462 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 19 Dec 2014 14:40:16 +0100 Subject: [PATCH] DATAMONGO-1118 - MappingMongoConverter now uses custom conversions for Map keys, too. We now allow conversions of map keys using custom Converter implementations if the conversion target type is a String. Original pull request: #260. --- .../core/convert/MappingMongoConverter.java | 19 ++- .../MappingMongoConverterUnitTests.java | 111 ++++++++++++++++++ 2 files changed, 127 insertions(+), 3 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 68dfc6296..3897819a2 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 @@ -626,9 +626,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App Object key = entry.getKey(); Object val = entry.getValue(); if (conversions.isSimpleType(key.getClass())) { - // Don't use conversion service here as removal of ObjectToString converter results in some primitive types not - // being convertable - String simpleKey = potentiallyEscapeMapKey(key.toString()); + + String simpleKey = potentiallyConvertMapKey(key); if (val == null || conversions.isSimpleType(val.getClass())) { writeSimpleInternal(val, dbo, simpleKey); } else if (val instanceof Collection || val.getClass().isArray()) { @@ -649,6 +648,20 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App return dbo; } + private String potentiallyConvertMapKey(Object key) { + + String stringKey = null; + if (key instanceof String + || !(conversions.hasCustomWriteTarget(key.getClass()) && conversions.getCustomWriteTarget(key.getClass()) + .equals(String.class))) { + stringKey = key.toString(); + } else { + stringKey = (String) getPotentiallyConvertedSimpleWrite(key); + } + + return potentiallyEscapeMapKey(stringKey); + } + /** * Potentially replaces dots in the given map key with the configured map key replacement if configured or aborts * conversion if none is configured. 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 3b9f0c0c0..8c546c25b 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 @@ -69,12 +69,15 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.aop.framework.ProxyFactory; +import org.springframework.beans.ConversionNotSupportedException; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; import org.springframework.core.convert.converter.Converter; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.annotation.TypeAlias; +import org.springframework.data.convert.ReadingConverter; +import org.springframework.data.convert.WritingConverter; import org.springframework.data.geo.Box; import org.springframework.data.geo.Circle; import org.springframework.data.geo.Distance; @@ -1898,6 +1901,68 @@ public class MappingMongoConverterUnitTests { Mockito.any(DbRefResolverCallback.class)); } + /** + * @see DATAMONGO-1118 + */ + @Test + public void convertsMapKeyUsingCustomConverterForAndBackwards() { + + MappingMongoConverter converter = new MappingMongoConverter(resolver, mappingContext); + converter.setCustomConversions(new CustomConversions(Arrays.asList(new FooBarEnumToStringConverter(), + new StringToFooNumConverter()))); + converter.afterPropertiesSet(); + + ClassWithMapUsingEnumAsKey source = new ClassWithMapUsingEnumAsKey(); + source.map = new HashMap(); + source.map.put(FooBarEnum.FOO, "wohoo"); + + DBObject target = new BasicDBObject(); + converter.write(source, target); + + assertThat(converter.read(ClassWithMapUsingEnumAsKey.class, target).map, equalTo(source.map)); + } + + /** + * @see DATAMONGO-1118 + */ + @Test + public void writesMapKeyUsingCustomConverter() { + + MappingMongoConverter converter = new MappingMongoConverter(resolver, mappingContext); + converter.setCustomConversions(new CustomConversions(Arrays.asList(new FooBarEnumToStringConverter()))); + converter.afterPropertiesSet(); + + ClassWithMapUsingEnumAsKey source = new ClassWithMapUsingEnumAsKey(); + source.map = new HashMap(); + source.map.put(FooBarEnum.FOO, "spring"); + source.map.put(FooBarEnum.BAR, "data"); + + DBObject target = new BasicDBObject(); + converter.write(source, target); + + DBObject map = DBObjectTestUtils.getAsDBObject(target, "map"); + + assertThat(map.containsField("foo-enum-value"), is(true)); + assertThat(map.containsField("bar-enum-value"), is(true)); + } + + /** + * @see DATAMONGO-1118 + */ + @Test + public void readsMapKeyUsingCustomConverter() { + + MappingMongoConverter converter = new MappingMongoConverter(resolver, mappingContext); + converter.setCustomConversions(new CustomConversions(Arrays.asList(new StringToFooNumConverter()))); + converter.afterPropertiesSet(); + + DBObject source = new BasicDBObject("map", new BasicDBObject("foo-enum-value", "spring")); + + ClassWithMapUsingEnumAsKey target = converter.read(ClassWithMapUsingEnumAsKey.class, source); + + assertThat(target.map.get(FooBarEnum.FOO), is("spring")); + } + static class GenericType { T content; } @@ -2160,4 +2225,50 @@ public class MappingMongoConverterUnitTests { } } + + static enum FooBarEnum { + FOO, BAR; + } + + static class ClassWithMapUsingEnumAsKey { + Map map; + } + + @WritingConverter + static class FooBarEnumToStringConverter implements Converter { + + @Override + public String convert(FooBarEnum source) { + if (source == null) { + return null; + } + + return FooBarEnum.FOO.equals(source) ? "foo-enum-value" : "bar-enum-value"; + } + + } + + @ReadingConverter + static class StringToFooNumConverter implements Converter { + + @Override + public FooBarEnum convert(String source) { + + if (source == null) { + return null; + } + + if (source.equals("foo-enum-value")) { + return FooBarEnum.FOO; + } + if (source.equals("bar-enum-value")) { + return FooBarEnum.BAR; + } + + throw new ConversionNotSupportedException(source, String.class, null); + } + + } + + }