/** * Reads the given {@link DBObject} into a {@link Map}. will recursively resolve nested {@link * Map}s as well. * * @param type the {@link Map} {@link TypeInformation} to be used to unmarshall this {@link * DBObject}. * @param dbObject must not be {@literal null} * @param path must not be {@literal null} * @return */ @SuppressWarnings("unchecked") protected Map<Object, Object> readMap( TypeInformation<?> type, DBObject dbObject, ObjectPath path) { Assert.notNull(dbObject, "DBObject must not be null!"); Assert.notNull(path, "Object path must not be null!"); Class<?> mapType = typeMapper.readType(dbObject, type).getType(); TypeInformation<?> keyType = type.getComponentType(); Class<?> rawKeyType = keyType == null ? null : keyType.getType(); TypeInformation<?> valueType = type.getMapValueType(); Class<?> rawValueType = valueType == null ? null : valueType.getType(); Map<Object, Object> map = CollectionFactory.createMap(mapType, rawKeyType, dbObject.keySet().size()); Map<String, Object> sourceMap = dbObject.toMap(); for (Entry<String, Object> entry : sourceMap.entrySet()) { if (typeMapper.isTypeKey(entry.getKey())) { continue; } Object key = potentiallyUnescapeMapKey(entry.getKey()); if (rawKeyType != null) { key = conversionService.convert(key, rawKeyType); } Object value = entry.getValue(); if (value instanceof DBObject) { map.put(key, read(valueType, (DBObject) value, path)); } else if (value instanceof DBRef) { map.put( key, DBRef.class.equals(rawValueType) ? value : read(valueType, readRef((DBRef) value))); } else { Class<?> valueClass = valueType == null ? null : valueType.getType(); map.put(key, getPotentiallyConvertedSimpleRead(value, valueClass)); } } return map; }
/** * Adds custom type information to the given {@link DBObject} if necessary. That is if the value * is not the same as the one given. This is usually the case if you store a subtype of the actual * declared type of the property. * * @param type * @param value must not be {@literal null}. * @param dbObject must not be {@literal null}. */ protected void addCustomTypeKeyIfNecessary( TypeInformation<?> type, Object value, DBObject dbObject) { TypeInformation<?> actualType = type != null ? type.getActualType() : null; Class<?> reference = actualType == null ? Object.class : actualType.getType(); Class<?> valueType = ClassUtils.getUserClass(value.getClass()); boolean notTheSameClass = !valueType.equals(reference); if (notTheSameClass) { typeMapper.writeType(valueType, dbObject); } }
/** * Root entry method into write conversion. Adds a type discriminator to the {@link DBObject}. * Shouldn't be called for nested conversions. * * @see org.springframework.data.mongodb.core.core.convert.MongoWriter#write(java.lang.Object, * com.mongodb.DBObject) */ public void write(final Object obj, final DBObject dbo) { if (null == obj) { return; } Class<?> entityType = obj.getClass(); boolean handledByCustomConverter = conversions.getCustomWriteTarget(entityType, DBObject.class) != null; TypeInformation<? extends Object> type = ClassTypeInformation.from(entityType); if (!handledByCustomConverter && !(dbo instanceof BasicDBList)) { typeMapper.writeType(type, dbo); } Object target = obj instanceof LazyLoadingProxy ? ((LazyLoadingProxy) obj).getTarget() : obj; writeInternal(target, dbo, type); }
@SuppressWarnings("unchecked") private <S extends Object> S read(TypeInformation<S> type, DBObject dbo, ObjectPath path) { if (null == dbo) { return null; } TypeInformation<? extends S> typeToUse = typeMapper.readType(dbo, type); Class<? extends S> rawType = typeToUse.getType(); if (conversions.hasCustomReadTarget(dbo.getClass(), rawType)) { return conversionService.convert(dbo, rawType); } if (DBObject.class.isAssignableFrom(rawType)) { return (S) dbo; } if (typeToUse.isCollectionLike() && dbo instanceof BasicDBList) { return (S) readCollectionOrArray(typeToUse, (BasicDBList) dbo, path); } if (typeToUse.isMap()) { return (S) readMap(typeToUse, dbo, path); } if (dbo instanceof BasicDBList) { throw new MappingException( String.format(INCOMPATIBLE_TYPES, dbo, BasicDBList.class, typeToUse.getType(), path)); } // Retrieve persistent entity info MongoPersistentEntity<S> persistentEntity = (MongoPersistentEntity<S>) mappingContext.getPersistentEntity(typeToUse); if (persistentEntity == null) { throw new MappingException("No mapping metadata found for " + rawType.getName()); } return read(persistentEntity, dbo, path); }
/** * Removes the type information from the entire conversion result. * * @param object * @param recursively whether to apply the removal recursively * @return */ private Object removeTypeInfo(Object object, boolean recursively) { if (!(object instanceof DBObject)) { return object; } DBObject dbObject = (DBObject) object; String keyToRemove = null; for (String key : dbObject.keySet()) { if (recursively) { Object value = dbObject.get(key); if (value instanceof BasicDBList) { for (Object element : (BasicDBList) value) { removeTypeInfo(element, recursively); } } else { removeTypeInfo(value, recursively); } } if (typeMapper.isTypeKey(key)) { keyToRemove = key; if (!recursively) { break; } } } if (keyToRemove != null) { dbObject.removeField(keyToRemove); } return dbObject; }