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.
This commit is contained in:
Christoph Strobl
2014-12-19 14:40:16 +01:00
committed by Oliver Gierke
parent 1c2964cab4
commit c353e02b3e
2 changed files with 128 additions and 3 deletions

View File

@@ -646,9 +646,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()) {
@@ -669,6 +668,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.

View File

@@ -56,12 +56,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;
@@ -1964,6 +1967,7 @@ public class MappingMongoConverterUnitTests {
assertThat(converter.read(TypeWithLocalDateTime.class, result).date, is(reference));
}
/**
* @see DATAMONGO-1128
*/
@@ -2001,6 +2005,68 @@ public class MappingMongoConverterUnitTests {
assertThat(read.localDateTime, is(Optional.of(now)));
}
/**
* @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<MappingMongoConverterUnitTests.FooBarEnum, String>();
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<MappingMongoConverterUnitTests.FooBarEnum, String>();
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> {
T content;
}
@@ -2303,4 +2369,50 @@ public class MappingMongoConverterUnitTests {
Optional<String> string = Optional.empty();
Optional<LocalDateTime> localDateTime = Optional.empty();
}
static enum FooBarEnum {
FOO, BAR;
}
static class ClassWithMapUsingEnumAsKey {
Map<FooBarEnum, String> map;
}
@WritingConverter
static class FooBarEnumToStringConverter implements Converter<FooBarEnum, String> {
@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<String, FooBarEnum> {
@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);
}
}
}