Skip to content

Commit d55505f

Browse files
committed
DATADOC-130 - Added custom converters for Locale and Character.
Register a custom converter for Locale and Character classes by default as we have to consider them simple (as they must not be inspected during mapping) but the have to be converted to String values before being handed over to Mongo.
1 parent de06029 commit d55505f

File tree

3 files changed

+63
-4
lines changed

3 files changed

+63
-4
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/AbstractMongoConverter.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,18 @@
2626
import java.util.HashSet;
2727
import java.util.Iterator;
2828
import java.util.List;
29+
import java.util.Locale;
2930
import java.util.Map;
3031
import java.util.Set;
3132

3233
import org.bson.types.ObjectId;
3334
import org.springframework.beans.factory.InitializingBean;
3435
import org.springframework.core.GenericTypeResolver;
3536
import org.springframework.core.convert.ConversionService;
37+
import org.springframework.core.convert.TypeDescriptor;
3638
import org.springframework.core.convert.converter.Converter;
3739
import org.springframework.core.convert.converter.ConverterFactory;
40+
import org.springframework.core.convert.converter.GenericConverter;
3841
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
3942
import org.springframework.core.convert.support.ConversionServiceFactory;
4043
import org.springframework.core.convert.support.GenericConversionService;
@@ -64,6 +67,7 @@ public AbstractMongoConverter(GenericConversionService conversionService) {
6467
this.conversionService = conversionService == null ? ConversionServiceFactory.createDefaultConversionService()
6568
: conversionService;
6669
this.conversionService.removeConvertible(Object.class, String.class);
70+
registerConverter(CustomToStringConverter.INSTANCE);
6771
}
6872

6973
/**
@@ -107,19 +111,33 @@ private void initializeConverters() {
107111
* @param converter
108112
*/
109113
private void registerConverter(Object converter) {
110-
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class);
111-
if (MONGO_TYPES.contains(arguments[1]) || MONGO_TYPES.contains(arguments[0])) {
112-
customTypeMapping.add(new ConvertiblePair(arguments[0], arguments[1]));
114+
115+
if (converter instanceof GenericConverter) {
116+
customTypeMapping.addAll(((GenericConverter) converter).getConvertibleTypes());
117+
} else {
118+
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class);
119+
if (MONGO_TYPES.contains(arguments[1]) || MONGO_TYPES.contains(arguments[0])) {
120+
customTypeMapping.add(new ConvertiblePair(arguments[0], arguments[1]));
121+
}
113122
}
123+
114124
boolean added = false;
125+
115126
if (converter instanceof Converter) {
116127
this.conversionService.addConverter((Converter<?, ?>) converter);
117128
added = true;
118129
}
130+
119131
if (converter instanceof ConverterFactory) {
120132
this.conversionService.addConverterFactory((ConverterFactory<?, ?>) converter);
121133
added = true;
122134
}
135+
136+
if (converter instanceof GenericConverter) {
137+
this.conversionService.addConverter((GenericConverter) converter);
138+
added = true;
139+
}
140+
123141
if (!added) {
124142
throw new IllegalArgumentException("Given set contains element that is neither Converter nor ConverterFactory!");
125143
}
@@ -222,4 +240,19 @@ public BasicDBList maybeConvertList(BasicDBList dbl) {
222240
}
223241
return newDbl;
224242
}
243+
244+
245+
private enum CustomToStringConverter implements GenericConverter {
246+
INSTANCE;
247+
248+
public Set<ConvertiblePair> getConvertibleTypes() {
249+
ConvertiblePair localeToString = new ConvertiblePair(Locale.class, String.class);
250+
ConvertiblePair booleanToString = new ConvertiblePair(Character.class, String.class);
251+
return new HashSet<ConvertiblePair>(Arrays.asList(localeToString, booleanToString));
252+
}
253+
254+
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
255+
return source.toString();
256+
}
257+
}
225258
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,14 @@ public TypeInformation<?> getValueType(TypeInformation<?> type) {
559559
*/
560560
private void writeSimpleInternal(String key, Object value, DBObject dbObject) {
561561

562-
Object valueToSet = value.getClass().isEnum() ? ((Enum<?>) value).name() : value;
562+
Class<?> customTarget = getCustomTarget(value.getClass(), null);
563+
564+
Object valueToSet = null;
565+
if (customTarget != null) {
566+
valueToSet = conversionService.convert(value, customTarget);
567+
} else {
568+
valueToSet = value.getClass().isEnum() ? ((Enum<?>) value).name() : value;
569+
}
563570
dbObject.put(key, valueToSet);
564571
}
565572

spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,21 @@ public void readsCollectionWithInterfaceCorrectly() {
269269

270270
}
271271

272+
@Test
273+
public void convertsLocalesOutOfTheBox() {
274+
LocaleWrapper wrapper = new LocaleWrapper();
275+
wrapper.locale = Locale.US;
276+
277+
DBObject dbObject = new BasicDBObject();
278+
converter.write(wrapper, dbObject);
279+
280+
Object localeField = dbObject.get("locale");
281+
assertThat(localeField, is(String.class));
282+
assertThat((String) localeField, is("en_US"));
283+
284+
LocaleWrapper read = converter.read(LocaleWrapper.class, dbObject);
285+
assertThat(read.locale, is(Locale.US));
286+
}
272287

273288
class ClassWithEnumProperty {
274289

@@ -307,6 +322,10 @@ class CollectionWrapper {
307322
List<Contact> contacts;
308323
}
309324

325+
class LocaleWrapper {
326+
Locale locale;
327+
}
328+
310329
private class LocalDateToDateConverter implements Converter<LocalDate, Date> {
311330

312331
public Date convert(LocalDate source) {

0 commit comments

Comments
 (0)