google / gson

A Java serialization/deserialization library to convert Java Objects into JSON and back
Apache License 2.0
23.13k stars 4.27k forks source link

TypeToken.getParameterized(Response.class, List.class, Long.class) get IllegalArgumentException: requires 1 type arguments, but got 2 #2703

Closed hfxu closed 3 weeks ago

hfxu commented 3 weeks ago

Gson version

2.11.0

Java / Android version

JDK17

Used tools

Maven

Description

When upgrade gson from 2.8.9 to 2.11.0, the below code not working with gson-2.11.0: Type responseType = TypeToken.getParameterized(Response.class, List.class, Long.class).getType(); And it's thrown a exception:

Exception in thread "main" java.lang.IllegalArgumentException: GsonTest$Response requires 1 type arguments, but got 2
    at com.google.gson.reflect.TypeToken.getParameterized(TypeToken.java:402)
    at com.waffo.backend.common.utils.GsonTest.main(GsonTest.java:13)

Expected behavior

Got a correct Type of Response<List<Long>>.

Actual behavior

Throw a exception like java.lang.IllegalArgumentException: GsonTest$Response requires 1 type arguments, but got 2

Reproduction steps

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import lombok.Data;

import java.lang.reflect.Type;
import java.util.List;

public class GsonTest {
    public static void main(String[] args) {
        String response = "{\"code\":\"000000\",\"data\":[148]}";
        Type responseType = TypeToken.getParameterized(Response.class, List.class, Long.class).getType();
        //would got exception at below line.
        Response<List<Long>> respObj = new Gson().fromJson(response, responseType);
        assert respObj.getData() != null && respObj.getData().size() == 1;
    }

    @Data
    static class Response<T> {
        private String code;
        private T data;
    }
}

Exception stack trace

Exception in thread "main" java.lang.IllegalArgumentException: GsonTest$Response requires 1 type arguments, but got 2
    at com.google.gson.reflect.TypeToken.getParameterized(TypeToken.java:402)
    at com.waffo.backend.common.utils.GsonTest.main(GsonTest.java:13)
eamonnmcmanus commented 3 weeks ago

It looks like this code is incorrect, but the older version of Gson didn't notice that. This commit from two years ago added validation that the number of type arguments given to TypeToken.getParameterized is equal to the number of type parameters that the base type actually has. Here, Response has only one type argument, <T>, but the call to TypeToken.getParameterized is giving it two, <List, Long>. I think what you want is something like this:

Type listOfLong = TypeToken.getParameterized(List.class, Long.class).getType();
Type responseType = TypeToken.getParameterized(Response.class, listOfLong);
Marcono1234 commented 3 weeks ago

Or alternatively create an anonymous TypeToken subclass:

var responseType = new TypeToken<Response<List<Long>>>() {};

This prevents such malformed type issues already at compile time.

You can then also use the T Gson.fromJson(..., TypeToken<T>) overloads which were added in Gson 2.10. These are more type safe compared to the Gson.fromJson(..., Type) overloads.

hfxu commented 3 weeks ago

Hi @Marcono1234 @eamonnmcmanus Thank you for your suggestion! It appears that I had been used the method in incorrect way all along.