Affects: 2.4.0-SNAPSHOT Affects: Spring boot 2.3.4.RELEASE


Same behavior with kotlin version 1.3.72 and 1.4.10 Tested with java 8


I try to map a GET request into a pojo class in kotlin. The method param resolver fail to map properly the query params.

I used to have no problem to do this mapping in Java; I am not sure anymore but I think it was working before for Kotlin with Set/List/etc. Now it does not work anymore.

I don't think I have to implement my own HandlerMethodArgumentResolver for all my custom criterias, I use only basic fields, and java works but not kotlin.

I know about StringToCollectionConverter, DelimitedStringToCollectionConverter and etc but it does not seem to be working with Kotlin Data class (or normal class)


Do a get with: localhost:8080/test/param/test-1?projectIdIn2=1,2,3&projectIdIn3=1,2,4&projectIdIn4=1,2,5&projectIdIn5=1,2,6&projectIdIn6=1,2,7&projectIdIn8=1,2,9&projectIdIn1=1&projectIdIn1=3

Fail with 2020-09-25 15:36:19.865 WARN 10780 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.ModelAttributeMethodProcessor$1: org.springframework.validation.BeanPropertyBindingResult: 2 errors Field error in object 'simpleCriteria' on field 'projectIdIn3': rejected value [1,2,4]; codes [typeMismatch.simpleCriteria.projectIdIn3,typeMismatch.projectIdIn3,typeMismatch.java.util.Set,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [simpleCriteria.projectIdIn3,projectIdIn3]; arguments []; default message [projectIdIn3]]; default message [Failed to convert value of type 'java.lang.String[]' to required type 'java.util.Set'; nested exception is java.lang.NumberFormatException: For input string: "1,2,4"] Field error in object 'simpleCriteria' on field 'projectIdIn5': rejected value [1,2,6]; codes [typeMismatch.simpleCriteria.projectIdIn5,typeMismatch.projectIdIn5,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [simpleCriteria.projectIdIn5,projectIdIn5]; arguments []; default message [projectIdIn5]]; default message [Failed to convert value of type 'java.lang.String[]' to required type 'java.util.List'; nested exception is java.lang.NumberFormatException: For input string: "1,2,6"]]


Same request as above but with java pojo: localhost:8080/test/param/test-java-1?projectIdIn2=1,2,3&projectIdIn3=1,2,4&projectIdIn4=1,2,5&projectIdIn5=1,2,6&projectIdIn6=1,2,7&projectIdIn8=1,2,9&projectIdIn1=1&projectIdIn1=3

It will return a JSON: {"projectIdIn1":[1,3],"projectIdIn2":["1","2","3"],"projectIdIn5":[1,2,6],"projectIdIn6":["1","2","7"]}


Request for: localhost:8080/test/param/test-3?projectIdIn2=1,2,3&projectIdIn3=1,2,4&projectIdIn4=1,2,5&projectIdIn5=1,2,6&projectIdIn6=1,2,7&projectIdIn7=1,2,8&projectIdIn8=1,2,9&enum2=beijing,shanghai&projectIdIn1=5&projectIdIn1=6

Result: {"projectIdIn1":[5,6],"projectIdIn2":["1","2","3"],"projectIdIn3":[1,2,4],"projectIdIn4":["1","2","5"],"projectIdIn5":[1,2,6],"projectIdIn6":["1","2","7"],"projectIdIn7":[1,2,8],"projectIdIn8":["1","2","9"],"enum1":null,"enum2":["beijing","shanghai"],"enum3":null}



@RestController
@RequestMapping("/test/param")
class TestController {

    @GetMapping("/test-java-1")
    fun test1(criteria: JavaCriteria): JavaCriteria {
        return criteria
    }

    @GetMapping("/test-1")
    fun test1(criteria: SimpleCriteria): SimpleCriteria {
        return criteria
    }

    @GetMapping("/test-3")
    fun test3(
            @RequestParam projectIdIn1: Set<Long>? = null,
            @RequestParam projectIdIn2: Set<String>? = null,
            @RequestParam projectIdIn3: Set<ProjectId>? = null,
            @RequestParam projectIdIn4: Set<ProjectIdNew>? = null,
            @RequestParam projectIdIn5: List<Long>? = null,
            @RequestParam projectIdIn6: List<String>? = null,
            @RequestParam projectIdIn7: List<ProjectId>? = null,
            @RequestParam projectIdIn8: List<ProjectIdNew>? = null
    ): SimpleCriteria {
        return SimpleCriteria(
                projectIdIn1,
                projectIdIn2,
                projectIdIn3,
                projectIdIn4,
                projectIdIn5,
                projectIdIn6,
                projectIdIn7,
                projectIdIn8
        )
    }

}

typealias ProjectId = Long
typealias ProjectIdNew = String

data class SimpleCriteria(
        val projectIdIn1: Set<Long>? = null, // fails to convert 1,2
        val projectIdIn2: Set<String>? = null, // Get a list with one element "1,2"
        val projectIdIn3: Set<ProjectId>? = null, // fails to convert 1,2
        val projectIdIn4: Set<ProjectIdNew>? = null, // Get a list with one element "1,2"
        val projectIdIn5: List<Long>? = null, // fails to convert 1,2
        val projectIdIn6: List<String>? = null, // Get a list with one element "1,2"
        val projectIdIn7: List<ProjectId>? = null, // fails to convert 1,2
        val projectIdIn8: List<ProjectIdNew>? = null // Get a list with one element "1,2"
)

public class JavaCriteria {

    private Set<Long> projectIdIn1;
    private Set<String> projectIdIn2;
    private List<Long> projectIdIn5;
    private List<String> projectIdIn6;

    public JavaCriteria() {
    }

    public Set<Long> getProjectIdIn1() {
        return projectIdIn1;
    }

    public void setProjectIdIn1(final Set<Long> projectIdIn1) {
        this.projectIdIn1 = projectIdIn1;
    }

    public Set<String> getProjectIdIn2() {
        return projectIdIn2;
    }

    public void setProjectIdIn2(final Set<String> projectIdIn2) {
        this.projectIdIn2 = projectIdIn2;
    }

    public List<Long> getProjectIdIn5() {
        return projectIdIn5;
    }

    public void setProjectIdIn5(final List<Long> projectIdIn5) {
        this.projectIdIn5 = projectIdIn5;
    }

    public List<String> getProjectIdIn6() {
        return projectIdIn6;
    }

    public void setProjectIdIn6(final List<String> projectIdIn6) {
        this.projectIdIn6 = projectIdIn6;
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        final JavaCriteria that = (JavaCriteria) o;
        return Objects.equals(projectIdIn1, that.projectIdIn1) &&
                Objects.equals(projectIdIn2, that.projectIdIn2) &&
                Objects.equals(projectIdIn5, that.projectIdIn5) &&
                Objects.equals(projectIdIn6, that.projectIdIn6);
    }

    @Override
    public int hashCode() {
        return Objects.hash(projectIdIn1, projectIdIn2, projectIdIn5, projectIdIn6);
    }

    @Override
    public String toString() {
        return "JavaCriteria{" +
                "projectIdIn1=" + projectIdIn1 +
                ", projectIdIn2=" + projectIdIn2 +
                ", projectIdIn5=" + projectIdIn5 +
                ", projectIdIn6=" + projectIdIn6 +
                '}';
    }
}

Comment From: bclozel

Duplicates spring-projects/spring-framework#25815 Please don't open multiple instances of the same issue.

Comment From: Blackdread

ok, I thought this issue might be more related to spring boot but not sure.