diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java index 3146d7215..bbe3c4e3d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/convert/MappingMongoConverter.java @@ -24,6 +24,7 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -415,7 +416,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App String name = prop.getFieldName(); if (prop.isCollection()) { - DBObject collectionInternal = writeCollectionInternal(prop, obj); + DBObject collectionInternal = writeCollectionInternal(prop, asCollection(obj)); dbo.put(name, collectionInternal); return; } @@ -449,48 +450,79 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App dbo.put(name, propDbObj); } - @SuppressWarnings("unchecked") - protected DBObject writeCollectionInternal(MongoPersistentProperty property, Object obj) { + /** + * Returns given object as {@link Collection}. Will return the {@link Collection} as is if the source is a + * {@link Collection} already, will convert an array into a {@link Collection} or simply create a single element + * collection for everything else. + * + * @param source + * @return + */ + private static Collection asCollection(Object source) { + + if (source instanceof Collection) { + return (Collection) source; + } + + return source.getClass().isArray() ? CollectionUtils.arrayToList(source) : Collections.singleton(source); + } + + + /** + * Writes the given {@link Collection} using the given {@link MongoPersistentProperty} information. + * + * @param property + * @param collection + * @return + */ + protected DBObject writeCollectionInternal(MongoPersistentProperty property, Collection collection) { + + if (!property.isDbReference()) { + return createCollectionDBObject(property.getTypeInformation(), collection); + } BasicDBList dbList = new BasicDBList(); - Class type = property.getType(); - Collection coll = type.isArray() ? CollectionUtils.arrayToList(obj) : (Collection) obj; - TypeInformation componentType = property.getTypeInformation().getComponentType(); - - for (Object element : coll) { + + for (Object element : collection) { if (element == null) { continue; } - TypeInformation valueType = ClassTypeInformation.from(element.getClass()); + DBRef dbRef = createDBRef(element, property.getDBRef()); + dbList.add(dbRef); + } + + return dbList; + } + + /** + * Creates a new {@link BasicDBList} from the given {@link Collection}. + * + * @param type + * @param source + * @return + */ + private BasicDBList createCollectionDBObject(TypeInformation type, Collection source) { + + BasicDBList dbList = new BasicDBList(); + TypeInformation componentType = type.getComponentType(); + + for (Object element : source) { - if (property.isDbReference()) { - DBRef dbRef = createDBRef(element, property.getDBRef()); - dbList.add(dbRef); - } else if (type.isArray() && isSimpleType(property.getComponentType())) { - dbList.add(element); - } else if (element instanceof List) { - List propObjColl = (List) element; - while (valueType.isCollectionLike()) { - valueType = valueType.getComponentType(); - } - if (isSimpleType(valueType.getType())) { - dbList.add(propObjColl); - } else { - BasicDBList propNestedDbList = new BasicDBList(); - for (Object propNestedObjItem : propObjColl) { - BasicDBObject propDbObj = new BasicDBObject(); - writeInternal(propNestedObjItem, propDbObj); - propNestedDbList.add(propDbObj); - } - dbList.add(propNestedDbList); - } - } else if (isSimpleType(element.getClass())) { + if (element == null) { + continue; + } + + Class elementType = element.getClass(); + + if (isSimpleType(elementType)) { dbList.add(element); + } else if (element instanceof Collection || elementType.isArray()) { + dbList.add(createCollectionDBObject(componentType, asCollection(element))); } else { BasicDBObject propDbObj = new BasicDBObject(); - writeInternal(element, propDbObj, mappingContext.getPersistentEntity(valueType)); + writeInternal(element, propDbObj, mappingContext.getPersistentEntity(ClassTypeInformation.from(element.getClass()))); addCustomTypeKeyIfNecessary(componentType, element, propDbObj); dbList.add(propDbObj); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java index 8f6d583d3..26c4452d2 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java @@ -19,6 +19,7 @@ package org.springframework.data.document.mongodb.mapping; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -314,6 +315,50 @@ public class MappingMongoConverterUnitTests { assertThat(nestedMap.get("afield"), is(firstLevel)); } + /** + * @see DATACMNS-42 + */ + @Test + public void writesClassWithBigDecimal() { + + BigDecimalContainer container = new BigDecimalContainer(); + container.value = BigDecimal.valueOf(2.5d); + + DBObject dbObject = new BasicDBObject(); + converter.write(container, dbObject); + + assertThat(dbObject.get("value"), is((Object) container.value)); + } + + /** + * @see DATACMNS-42 + */ + @Test + public void readsClassWithBigDecimal() { + + DBObject dbObject = new BasicDBObject("value", 2.5d); + BigDecimalContainer result = converter.read(BigDecimalContainer.class, dbObject); + + assertThat(result.value, is(BigDecimal.valueOf(2.5d))); + } + + @Test + public void writesNestedCollectionsCorrectly() { + + CollectionWrapper wrapper = new CollectionWrapper(); + wrapper.strings = Arrays.asList(Arrays.asList("Foo")); + + DBObject dbObject = new BasicDBObject(); + converter.write(wrapper, dbObject); + + Object outerStrings = dbObject.get("strings"); + assertThat(outerStrings, is(instanceOf(BasicDBList.class))); + + BasicDBList typedOuterString = (BasicDBList) outerStrings; + assertThat(typedOuterString.size(), is(1)); + + } + class ClassWithEnumProperty { SampleEnum sampleEnum; @@ -323,7 +368,7 @@ public class MappingMongoConverterUnitTests { FIRST, SECOND; } - public static class Address { + class Address { String street; String city; } @@ -332,14 +377,14 @@ public class MappingMongoConverterUnitTests { } - public static class Person implements Contact { + class Person implements Contact { LocalDate birthDate; @FieldName("foo") String firstname; } - static class ClassWithMapProperty { + class ClassWithMapProperty { Map map; } @@ -347,12 +392,17 @@ public class MappingMongoConverterUnitTests { Map>> nestedMaps; } - public static class BirthDateContainer { + class BirthDateContainer { LocalDate birthDate; } + class BigDecimalContainer { + BigDecimal value; + } + class CollectionWrapper { List contacts; + List> strings; } class LocaleWrapper {