Skip to content

MappingMongoConverter problem: ConversionContext#convert does not try to use custom converters first #3660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
xjme opened this issue May 30, 2021 · 1 comment
Assignees
Labels
type: regression A regression from a previous release

Comments

@xjme
Copy link

xjme commented May 30, 2021

Hi, we upgraded from spring boot 2.4.3 to version 2.5.0. In the new version of SB, the spring data mongo db was updated from version 3.1.5 to 3.2.1.

We encountered a problem during the conversion of an object implementing java.util.Map, for which, however, we have implemented a custom converter.

The problem is that the custom converter is not called, the default mapConverter is used instead.

When I compare the previous code of MappingMongoConverter, more precisely code related to converting a property value, it was first checked whether a value with a certain type has a custom conversion defined. This is not the case in the new version of spring data mongodb, more precisely within ConversionContext#convert method.

For clarity, I present the differences between the individual versions of MappingMongoConvertor:

Spring Data MongoDb 3.2.1

MappingMongoConverter#getPropertyValue

@Nullable
@SuppressWarnings("unchecked")
public <T> T getPropertyValue(MongoPersistentProperty property) {

	String expression = property.getSpelExpression();
	Object value = expression != null ? evaluator.evaluate(expression) : accessor.get(property);

	if (value == null) {
		return null;
	}

	return (T) context.convert(value, property.getTypeInformation());
}

MappingMongoConverter.ConversionContext#convert

public <S extends Object> S convert(Object source, TypeInformation<? extends S> typeHint) {

	Assert.notNull(typeHint, "TypeInformation must not be null");

	if (source instanceof Collection) {

		Class<?> rawType = typeHint.getType();
		if (!Object.class.equals(rawType)) {
			if (!rawType.isArray() && !ClassUtils.isAssignable(Iterable.class, rawType)) {
				throw new MappingException(
						String.format(INCOMPATIBLE_TYPES, source, source.getClass(), rawType, getPath()));
			}
		}

		if (typeHint.isCollectionLike() || typeHint.getType().isAssignableFrom(Collection.class)) {
			return (S) collectionConverter.convert(this, (Collection<?>) source, typeHint);
		}
	}

	if (typeHint.isMap()) { // this causes a problem, it should be converted using custom map converter, not this.mapConverter
		return (S) mapConverter.convert(this, (Bson) source, typeHint);
	}

	if (source instanceof DBRef) {
		return (S) dbRefConverter.convert(this, (DBRef) source, typeHint);
	}

	if (source instanceof Collection) {
		throw new MappingException(
				String.format(INCOMPATIBLE_TYPES, source, BasicDBList.class, typeHint.getType(), getPath()));
	}

	if (source instanceof Bson) {
		return (S) documentConverter.convert(this, (Bson) source, typeHint);
	}

	return (S) elementConverter.convert(source, typeHint);
}

vs.

Spring Data MongoDb 3.1.5

MappingMongoConverter#getPropertyValue

@Nullable
public <T> T getPropertyValue(MongoPersistentProperty property) {

	String expression = property.getSpelExpression();
	Object value = expression != null ? evaluator.evaluate(expression) : accessor.get(property);

	if (value == null) {
		return null;
	}

	return readValue(value, property.getTypeInformation(), path);
}

MappingMongoConverter#readValue

@Nullable
@SuppressWarnings("unchecked")
<T> T readValue(Object value, TypeInformation<?> type, ObjectPath path) {

	Class<?> rawType = type.getType();

	if (conversions.hasCustomReadTarget(value.getClass(), rawType)) { // this check picks the custom converter
		return (T) conversionService.convert(value, rawType);
	} else if (value instanceof DBRef) {
		return potentiallyReadOrResolveDbRef((DBRef) value, type, path, rawType);
	} else if (value instanceof List) {
		return (T) readCollectionOrArray(type, (List<Object>) value, path);
	} else if (value instanceof Document) {
		return (T) read(type, (Document) value, path);
	} else if (value instanceof DBObject) {
		return (T) read(type, (BasicDBObject) value, path);
	} else {
		return (T) getPotentiallyConvertedSimpleRead(value, rawType);
	}
}
@christophstrobl
Copy link
Member

thanks for reporting! we'll look into it.

@christophstrobl christophstrobl self-assigned this May 31, 2021
@christophstrobl christophstrobl added the type: regression A regression from a previous release label May 31, 2021
mp911de pushed a commit that referenced this issue Jun 9, 2021
mp911de added a commit that referenced this issue Jun 9, 2021
Reformat code.

See #3660.
Original pull request: #3662.
@mp911de mp911de closed this as completed in ece261a Jun 9, 2021
mp911de added a commit that referenced this issue Jun 9, 2021
Reformat code.

See #3660.
Original pull request: #3662.
@mp911de mp911de added this to the 3.2.2 (2021.0.2) milestone Jun 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: regression A regression from a previous release
Projects
None yet
3 participants