diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 0d7fb7449..7d8e59dd5 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -152,6 +152,13 @@ junit test + + + joda-time + joda-time + 1.6 + test + diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/SimpleMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/SimpleMongoConverter.java index 404226d45..11661eed6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/SimpleMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/SimpleMongoConverter.java @@ -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> MONGO_TYPES = Arrays.asList(Number.class, Date.class, String.class, DBObject.class); private static final Set 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 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 convertibleTypes = ((ConditionalGenericConverter) converter).getConvertibleTypes(); - if (REFERENCE.equals(convertibleTypes)) { - return null; - } - } - - return converter; - } - } /** * Simple singleton to convert {@link ObjectId}s to their {@link String} representation. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java index 892f1b3e5..4ca57c3ae 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/SimpleMongoConverterTests.java @@ -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> converters = new HashSet>(); + 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> 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 { + + public Date convert(LocalDate source) { + return source.toDateMidnight().toDate(); + } + } + + private class DateToLocalDateConverter implements Converter { + + public LocalDate convert(Date source) { + return new LocalDate(source.getTime()); + } + } }