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 35dc35d66..6b3ac83c5 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 @@ -79,6 +79,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App private static final List> VALID_ID_TYPES = Arrays.asList(new Class[] { ObjectId.class, String.class, BigInteger.class, byte[].class }); + private static final TypeInformation MAP_TYPE_INFORMATION = ClassTypeInformation.from(Map.class); + protected static final Log log = LogFactory.getLog(MappingMongoConverter.class); protected final MappingContext, MongoPersistentProperty> mappingContext; @@ -174,6 +176,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App } return conversionService.convert(l, rawType); } + + if (typeToUse.isMap()) { + return (S) readMap(typeToUse, dbo); + } // Retrieve persistent entity info MongoPersistentEntity persistentEntity = (MongoPersistentEntity) mappingContext @@ -638,6 +644,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App @SuppressWarnings({ "rawtypes", "unchecked" }) private Object getPotentiallyConvertedSimpleRead(Object value, Class target) { + Assert.notNull(target); + if (value == null) { return null; } @@ -761,10 +769,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App @SuppressWarnings("unchecked") protected Map readMap(TypeInformation type, DBObject dbObject) { - Assert.notNull(type); - Assert.isTrue(type.isMap()); Assert.notNull(dbObject); - + + type = type == null ? MAP_TYPE_INFORMATION : type; Class customMapType = findTypeToBeUsed(dbObject); Class mapType = customMapType == null ? type.getType() : customMapType; @@ -776,19 +783,26 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App continue; } - Class keyType = type.getComponentType().getType(); - Object key = conversionService.convert(entry.getKey(), keyType); + Object key = entry.getKey(); + + TypeInformation keyTypeInformation = type.getComponentType(); + if (keyTypeInformation != null) { + Class keyType = keyTypeInformation.getType(); + key = conversionService.convert(entry.getKey(), keyType); + } + + Object value = entry.getValue(); + TypeInformation valueType = type.getMapValueType(); + valueType = valueType == null ? MAP_TYPE_INFORMATION : valueType; - if (null != entry.getValue() && entry.getValue() instanceof DBObject) { - - DBObject valueSource = (DBObject) entry.getValue(); - TypeInformation valueType = type.getMapValueType(); - - Object value = valueType.isMap() ? readMap(valueType, valueSource) : read(valueType, valueSource); - - map.put(key, value); + if (value instanceof BasicDBList) { + BasicDBList list =(BasicDBList) value; + map.put(key, read(valueType, list)); + } else if (value instanceof DBObject) { + DBObject valueSource = (DBObject) value; + map.put(key, valueType.isMap() ? readMap(valueType, valueSource) : read(valueType, valueSource)); } else { - map.put(key, getPotentiallyConvertedSimpleRead(entry.getValue(), type.getMapValueType().getType())); + map.put(key, getPotentiallyConvertedSimpleRead(value, valueType.getType())); } } @@ -835,9 +849,13 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App private TypeInformation getMoreConcreteTargetType(DBObject dbObject, TypeInformation basicType) { Class documentsTargetType = findTypeToBeUsed(dbObject); Class rawType = basicType.getType(); + + if (documentsTargetType == null && Object.class.equals(rawType)) { + return (TypeInformation) MAP_TYPE_INFORMATION; + } + boolean isMoreConcreteCustomType = documentsTargetType != null && rawType.isAssignableFrom(documentsTargetType); - return isMoreConcreteCustomType ? (TypeInformation) ClassTypeInformation.from(documentsTargetType) - : basicType; + return isMoreConcreteCustomType ? (TypeInformation) ClassTypeInformation.from(documentsTargetType) : basicType; } protected List unwrapList(BasicDBList dbList, TypeInformation targetType) { 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 c8f5513d4..c3ed8e872 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 @@ -605,6 +605,24 @@ public class MappingMongoConverterUnitTests { assertThat(result.mapOfObjects, is(not(nullValue()))); } + /** + * @see DATADOC-245 + */ + @Test + public void readsMapListNestedValuesCorrectly() { + + BasicDBList list = new BasicDBList(); + BasicDBObject nested = new BasicDBObject(); + nested.append("Hello", "World"); + list.add(nested); + DBObject source = new BasicDBObject("mapOfObjects", new BasicDBObject("Foo", list)); + + ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, source); + Object firstObjectInFoo = ((List) result.mapOfObjects.get("Foo")).get(0); + assertThat(firstObjectInFoo, is(instanceOf(Map.class))); + assertThat((String)((Map) firstObjectInFoo).get("Hello"), is(equalTo("World"))); + } + class GenericType { T content; }