diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java index 1fdd9f286e1..6a8b3f6bdeb 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -348,6 +348,13 @@ public class GenericConversionService implements ConfigurableConversionService { conditionalConverter.matches(sourceType, targetType); } + public boolean matchesFallback(TypeDescriptor sourceType, TypeDescriptor targetType) { + return (this.typeInfo.getTargetType() == targetType.getObjectType() && + this.targetType.hasUnresolvableGenerics() && + (!(this.converter instanceof ConditionalConverter conditionalConverter) || + conditionalConverter.matches(sourceType, targetType))); + } + @Override @Nullable public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { @@ -622,12 +629,20 @@ public class GenericConversionService implements ConfigurableConversionService { @Nullable public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { + // Look for proper match among all converters (taking full generics into account) for (GenericConverter converter : this.converters) { if (!(converter instanceof ConditionalGenericConverter genericConverter) || genericConverter.matches(sourceType, targetType)) { return converter; } } + // Fallback to pre-6.2.3 behavior: accept Class match for unresolvable generics + for (GenericConverter converter : this.converters) { + if (converter instanceof ConverterAdapter converterAdapter && + converterAdapter.matchesFallback(sourceType, targetType)) { + return converter; + } + } return null; } diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java index b5155635dc2..6fe83b0aaa2 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java @@ -581,6 +581,18 @@ class GenericConversionServiceTests { assertThat(bList).allMatch(e -> e instanceof BRaw); } + @Test + void stringToListOfMapConverterWithFallbackMatch() { + conversionService.addConverter(new StringToListOfMapConverter()); + + List> result = (List>) conversionService.convert("foo", + TypeDescriptor.valueOf(String.class), + TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Map.class)) + ); + + assertThat("foo").isEqualTo(result.get(0).get("bar")); + } + @ExampleAnnotation(active = true) public String annotatedString; @@ -971,4 +983,13 @@ class GenericConversionServiceTests { } } + + private static class StringToListOfMapConverter implements Converter>> { + + @Override + public List> convert(String source) { + return List.of(Map.of("bar", source)); + } + } + }