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:
Oliver Gierke
2011-03-17 09:36:01 +01:00
parent 74df4349ed
commit 611793d5c7
3 changed files with 114 additions and 36 deletions

View File

@@ -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>

View File

@@ -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.

View File

@@ -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());
}
}
}