Skip to content

GenericConversionService cannot find a converter when converting to a Kotlin list of maps #34535

@dmitrysulman

Description

@dmitrysulman

This test works fine in Spring 6.2.2 but fails in 6.2.3.

It is probably related to this fix: #34298.

package com.example import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.springframework.core.convert.TypeDescriptor import org.springframework.core.convert.converter.Converter import org.springframework.core.convert.support.GenericConversionService class GenericConversionServiceTest { private val conversionService = GenericConversionService() @Test fun stringToListOfStringToAnyMapsConverterTest() { conversionService.addConverter(StringToListOfStringToAnyMapsConverter) val result = conversionService.convert( "foo", TypeDescriptor.valueOf(String::class.java), TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(Map::class.java)) ) as List<Map<String, Any>> assertEquals("foo", result.first()["bar"]) } } object StringToListOfStringToAnyMapsConverter : Converter<String, List<Map<String, Any>>> { override fun convert(source: String): List<Map<String, Any>> { return listOf(mapOf("bar" to source)) } }

Exception:

No converter found capable of converting from type [java.lang.String] to type [java.util.List<java.util.Map<?, ?>>] org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.util.List<java.util.Map<?, ?>>]	at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:294)	at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:185)	at com.medisafe.chp.client.service.GenericConversionServiceTest.stringToListOfStringToAnyMapsConverterTest(GenericConversionServiceTest.kt:17)	at java.base/java.lang.reflect.Method.invoke(Method.java:580)	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) 

After a brief investigation, we found that this issue may be related to Kotlin declaration-site variance. Kotlin's List is declared with <out E>:

public actual interface List<out E> : Collection<E>

As a result, Converter<String, List<Map<String, Any>>> compiles to something like Converter<String, List<? extends Map<String, ?>>>. The fix introduced in #34298 makes this converter incompatible with the target type List<String, Map<?, ?>>.

As a workaround we replaced Kotlin's List with Java equivalent in the converter declaration. The following converter works correctly:

object StringToListOfStringToAnyMapsConverter : Converter<String, java.util.List<Map<String, Any>>> { override fun convert(source: String): java.util.List<Map<String, Any>> { return listOf(mapOf("bar" to source)) as java.util.List<Map<String, Any>> } }

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: regressionA bug that is also a regression

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions