Improved handling of custom converters in SimpleMongoConverter.
Explicitly remove converter for Object -> String on instance creation which allows us to get rid of the ugly SimpleToStringSuppressingGenericConversionService. writeCompoundValue(…) now asks the ConversionService whether it can convert the value to be written into one of Mongos native primitive types and rather delegates to the service instead of recursively writing the value itself.
This commit is contained in:
@@ -152,6 +152,13 @@
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>1.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
|
||||
@@ -32,12 +32,9 @@ import org.bson.types.ObjectId;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterFactory;
|
||||
import org.springframework.core.convert.converter.GenericConverter;
|
||||
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.document.mongodb.MongoPropertyDescriptors.MongoPropertyDescriptor;
|
||||
@@ -54,6 +51,7 @@ import org.springframework.util.comparator.CompoundComparator;
|
||||
public class SimpleMongoConverter implements MongoConverter {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(SimpleMongoConverter.class);
|
||||
private static final List<Class<?>> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class, DBObject.class);
|
||||
private static final Set<String> SIMPLE_TYPES;
|
||||
|
||||
static {
|
||||
@@ -112,8 +110,8 @@ public class SimpleMongoConverter implements MongoConverter {
|
||||
* Creates a {@link SimpleMongoConverter}.
|
||||
*/
|
||||
public SimpleMongoConverter() {
|
||||
this.conversionService = new SimpleToStringSuppressingGenericConversionService();
|
||||
ConversionServiceFactory.addDefaultConverters(conversionService);
|
||||
this.conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
this.conversionService.removeConvertible(Object.class, String.class);
|
||||
initializeConverters();
|
||||
}
|
||||
|
||||
@@ -123,12 +121,10 @@ public class SimpleMongoConverter implements MongoConverter {
|
||||
*/
|
||||
protected void initializeConverters() {
|
||||
|
||||
|
||||
conversionService.addConverter(ObjectIdToStringConverter.INSTANCE);
|
||||
conversionService.addConverter(StringToObjectIdConverter.INSTANCE);
|
||||
conversionService.addConverter(ObjectIdToBigIntegerConverter.INSTANCE);
|
||||
conversionService.addConverter(BigIntegerToIdConverter.INSTANCE);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,15 +233,32 @@ public class SimpleMongoConverter implements MongoConverter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (conversionService.canConvert(value.getClass(), String.class)) {
|
||||
dbo.put(keyToUse, conversionService.convert(value, String.class));
|
||||
Class<?> customTargetType = getCustomTargetType(value);
|
||||
if (customTargetType != null) {
|
||||
dbo.put(keyToUse, conversionService.convert(value, customTargetType));
|
||||
return;
|
||||
}
|
||||
|
||||
DBObject nestedDbo = new BasicDBObject();
|
||||
write(value, nestedDbo);
|
||||
dbo.put(keyToUse, nestedDbo);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link ConversionService} has a custom {@link Converter} registered that can convert the given
|
||||
* object into one of the types supported by MongoDB.
|
||||
*
|
||||
* @param obj
|
||||
* @return
|
||||
*/
|
||||
private Class<?> getCustomTargetType(Object obj) {
|
||||
|
||||
for (Class<?> mongoType : MONGO_TYPES) {
|
||||
if (conversionService.canConvert(obj.getClass(), mongoType)) {
|
||||
return mongoType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -510,30 +523,6 @@ public class SimpleMongoConverter implements MongoConverter {
|
||||
public ObjectId convertObjectId(Object id) {
|
||||
return conversionService.convert(id, ObjectId.class);
|
||||
}
|
||||
|
||||
private static class SimpleToStringSuppressingGenericConversionService extends GenericConversionService {
|
||||
|
||||
private static final Set<ConvertiblePair> REFERENCE = Collections.singleton(new ConvertiblePair(Object.class, String.class));
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.support.GenericConversionService#getConverter(org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor)
|
||||
*/
|
||||
@Override
|
||||
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
|
||||
GenericConverter converter = super.getConverter(sourceType, targetType);
|
||||
|
||||
if (converter instanceof ConditionalGenericConverter) {
|
||||
|
||||
Set<ConvertiblePair> convertibleTypes = ((ConditionalGenericConverter) converter).getConvertibleTypes();
|
||||
if (REFERENCE.equals(convertibleTypes)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return converter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple singleton to convert {@link ObjectId}s to their {@link String} representation.
|
||||
|
||||
@@ -21,15 +21,22 @@ import static org.junit.Assert.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.util.JSON;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.document.mongodb.SomeEnumTest.NumberEnum;
|
||||
import org.springframework.data.document.mongodb.SomeEnumTest.StringEnum;
|
||||
import org.springframework.data.document.mongodb.convert.SimpleMongoConverter;
|
||||
@@ -292,10 +299,50 @@ public class SimpleMongoConverterTests {
|
||||
(DBObject) JSON.parse("{\"_id\" : {\"$oid\" : \"4d24809660413b687f5d323e\" }}"));
|
||||
assertThat(result.getId().toString(16), is("4d24809660413b687f5d323e"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsAddressCorrectly() {
|
||||
|
||||
Address address = new Address();
|
||||
address.city = "New York";
|
||||
address.street = "Broadway";
|
||||
|
||||
DBObject dbObject = new BasicDBObject();
|
||||
|
||||
converter.write(address, dbObject);
|
||||
|
||||
assertThat(dbObject.get("city").toString(), is("New York"));
|
||||
assertThat(dbObject.get("street").toString(), is("Broadway"));
|
||||
|
||||
Address result = converter.read(Address.class, dbObject);
|
||||
assertThat(result.city, is("New York"));
|
||||
assertThat(result.street, is("Broadway"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertsJodaTimeTypesCorrectly() {
|
||||
|
||||
Set<Converter<?, ?>> converters = new HashSet<Converter<?,?>>();
|
||||
converters.add(new LocalDateToDateConverter());
|
||||
converters.add(new DateToLocalDateConverter());
|
||||
|
||||
converter.setConverters(converters);
|
||||
|
||||
AnotherPerson person = new AnotherPerson();
|
||||
person.birthDate = new LocalDate();
|
||||
|
||||
DBObject dbObject = new BasicDBObject();
|
||||
converter.write(person, dbObject);
|
||||
|
||||
assertTrue(dbObject.get("birthDate") instanceof Date);
|
||||
|
||||
AnotherPerson result = converter.read(AnotherPerson.class, dbObject);
|
||||
assertThat(result.getBirthDate(), is(notNullValue()));
|
||||
}
|
||||
|
||||
private void assertListOfStringAndLong(List<Class<?>> types) {
|
||||
|
||||
assertThat(types.size(), is(2));
|
||||
assertThat(types.size(), CoreMatchers.is(2));
|
||||
assertEquals(String.class, types.get(0));
|
||||
assertEquals(Long.class, types.get(1));
|
||||
}
|
||||
@@ -347,4 +394,39 @@ public class SimpleMongoConverterTests {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Address {
|
||||
String street;
|
||||
String city;
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AnotherPerson {
|
||||
LocalDate birthDate;
|
||||
|
||||
public LocalDate getBirthDate() {
|
||||
return birthDate;
|
||||
}
|
||||
}
|
||||
|
||||
private class LocalDateToDateConverter implements Converter<LocalDate, Date> {
|
||||
|
||||
public Date convert(LocalDate source) {
|
||||
return source.toDateMidnight().toDate();
|
||||
}
|
||||
}
|
||||
|
||||
private class DateToLocalDateConverter implements Converter<Date, LocalDate> {
|
||||
|
||||
public LocalDate convert(Date source) {
|
||||
return new LocalDate(source.getTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user