Top-gg-Community / java-sdk

An API wrapper for https://top.gg/api/docs that works in Java
Apache License 2.0
36 stars 22 forks source link

gson cannot deserialise to Void #18

Open robinfriedli opened 1 year ago

robinfriedli commented 1 year ago

setStats creates a ResponseTransformer that attempts to deserialise the response to java.lang.Void:

    private CompletionStage<Void> setStats(JSONObject jsonBody) {
        HttpUrl url = baseUrl.newBuilder()
                .addPathSegment("bots")
                .addPathSegment(botId)
                .addPathSegment("stats")
                .build();

        return post(url, jsonBody, Void.class);
    }

    private <E> CompletionStage<E> post(HttpUrl url, JSONObject jsonBody, Class<E> aClass) {
        return post(url, jsonBody, new DefaultResponseTransformer<>(aClass, gson));
    }

public class DefaultResponseTransformer<E> implements ResponseTransformer<E> {

    private final Class<E> aClass;
    private final Gson gson;

    public DefaultResponseTransformer(Class<E> aClass, Gson gson) {
        this.aClass = aClass;
        this.gson = gson;
    }

    @Override
    public E transform(Response response) throws IOException {
        String body = response.body().string();
        return gson.fromJson(body, aClass);
    }

}

However, this does not work on newer versions of java because java.base is a sealed module:

com.google.gson.JsonIOException: Failed making constructor 'java.lang.Void#Void()' accessible; either change its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type: Unable to make private java.lang.Void() accessible: module java.base does not "opens java.lang" to unnamed module @45d84a20
    at com.google.gson.internal.ConstructorConstructor$3.construct(ConstructorConstructor.java:131)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:211)
    at com.google.gson.Gson.fromJson(Gson.java:991)
    at com.google.gson.Gson.fromJson(Gson.java:956)
    at com.google.gson.Gson.fromJson(Gson.java:905)
    at com.google.gson.Gson.fromJson(Gson.java:876)
    at org.discordbots.api.client.io.DefaultResponseTransformer.transform(DefaultResponseTransformer.java:21)
    at org.discordbots.api.client.impl.DiscordBotListAPIImpl$1.onResponse(DiscordBotListAPIImpl.java:234)
    at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)

I don't think it makes sense anyway. If you are not interested in the response, why deserialise it? Probably better to not use a ResponseTransformer and complete the future with null when done instead of deserialising to Void.

robinfriedli commented 1 year ago

19 added a PR that adds a ResponseTransformer implementation that just returns null rather than trying to deserialise for null