From 2016aab9690fbe21eb9f706cb8060a647607ad86 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Mon, 15 Aug 2011 20:54:16 +0200 Subject: [PATCH] DATADOC-235 - Arbitrary Map values are now converted correctly. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Map values are now handled correctly regardless of the actual Map value type declaration (can be Object in the most open case). We now handle collections as Map value types correctly. BasicDBList instances are now hinted to become Lists by default (if not typed to another collection type by the property). Reduced visibility of MappingMongoConverter.addCustomTypeKeyIfNecessary(…) and made createCollectionDBObject(…) safe against null values for the TypeInformation. --- .../core/convert/MappingMongoConverter.java | 17 ++-- .../MappingMongoConverterUnitTests.java | 79 +++++++++++++++++++ 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index 60675a43e..8590cb64f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -149,7 +149,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App TypeInformation typeToUse = getMoreConcreteTargetType(dbo, type); Class rawType = typeToUse.getType(); - if (conversions.hasCustomReadTarget(DBObject.class, rawType)) { + if (conversions.hasCustomReadTarget(dbo.getClass(), rawType)) { return conversionService.convert(dbo, rawType); } @@ -513,14 +513,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App /** * Creates a new {@link BasicDBList} from the given {@link Collection}. * - * @param type - * @param source + * @param type the {@link TypeInformation} to consider or {@literal null} if unknown. + * @param source the collection to create a {@link BasicDBList} for, must not be {@literal null}. * @return */ private BasicDBList createCollectionDBObject(TypeInformation type, Collection source) { BasicDBList dbList = new BasicDBList(); - TypeInformation componentType = type.getComponentType(); + TypeInformation componentType = type == null ? null : type.getComponentType(); for (Object element : source) { @@ -556,6 +556,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App String simpleKey = key.toString(); if (val == null || conversions.isSimpleType(val.getClass())) { writeSimpleInternal(simpleKey, val, dbo); + } else if (val instanceof Collection) { + dbo.put(simpleKey, createCollectionDBObject(propertyType.getMapValueType(), (Collection) val)); } else { DBObject newDbo = new BasicDBObject(); writeInternal(val, newDbo); @@ -576,7 +578,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App * @param value * @param dbObject */ - public void addCustomTypeKeyIfNecessary(TypeInformation type, Object value, DBObject dbObject) { + protected void addCustomTypeKeyIfNecessary(TypeInformation type, Object value, DBObject dbObject) { if (type == null) { return; @@ -801,6 +803,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App * found. */ protected Class findTypeToBeUsed(DBObject dbObject) { + + if (dbObject instanceof BasicDBList) { + return List.class; + } + Object classToBeUsed = dbObject.get(CUSTOM_TYPE_KEY); if (classToBeUsed == null) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java index 821559104..c8f5513d4 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java @@ -528,6 +528,83 @@ public class MappingMongoConverterUnitTests { assertThat(converter.convertToMongoType(id), is((Object) id)); } + /** + * @see DATADOC-235 + */ + @Test + public void writesMapOfListsCorrectly() { + + ClassWithMapProperty input = new ClassWithMapProperty(); + input.mapOfLists = Collections.singletonMap("Foo", Arrays.asList("Bar")); + + BasicDBObject result = new BasicDBObject(); + converter.write(input, result); + + Object field = result.get("mapOfLists"); + assertThat(field, is(instanceOf(DBObject.class))); + + DBObject map = (DBObject) field; + Object foo = map.get("Foo"); + assertThat(foo, is(instanceOf(BasicDBList.class))); + + BasicDBList value = (BasicDBList) foo; + assertThat(value.size(), is(1)); + assertThat((String) value.get(0), is("Bar")); + } + + /** + * @see DATADOC-235 + */ + @Test + public void readsMapListValuesCorrectly() { + + BasicDBList list = new BasicDBList(); + list.add("Bar"); + DBObject source = new BasicDBObject("mapOfLists", new BasicDBObject("Foo", list)); + + ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, source); + assertThat(result.mapOfLists, is(not(nullValue()))); + } + + /** + * @see DATADOC-235 + */ + @Test + public void writesMapsOfObjectsCorrectly() { + + ClassWithMapProperty input = new ClassWithMapProperty(); + input.mapOfObjects = new HashMap(); + input.mapOfObjects.put("Foo", Arrays.asList("Bar")); + + BasicDBObject result = new BasicDBObject(); + converter.write(input, result); + + Object field = result.get("mapOfObjects"); + assertThat(field, is(instanceOf(DBObject.class))); + + DBObject map = (DBObject) field; + Object foo = map.get("Foo"); + assertThat(foo, is(instanceOf(BasicDBList.class))); + + BasicDBList value = (BasicDBList) foo; + assertThat(value.size(), is(1)); + assertThat((String) value.get(0), is("Bar")); + } + + /** + * @see DATADOC-235 + */ + @Test + public void readsMapOfObjectsListValuesCorrectly() { + + BasicDBList list = new BasicDBList(); + list.add("Bar"); + DBObject source = new BasicDBObject("mapOfObjects", new BasicDBObject("Foo", list)); + + ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, source); + assertThat(result.mapOfObjects, is(not(nullValue()))); + } + class GenericType { T content; } @@ -566,6 +643,8 @@ public class MappingMongoConverterUnitTests { class ClassWithMapProperty { Map map; + Map> mapOfLists; + Map mapOfObjects; } class ClassWithNestedMaps {