How do I put generic type for Gson's TypeToken?

霸气de小男生 提交于 2019-12-10 18:17:18

问题


EDIT After experimenting for a while, I know my problem is. I can't put generic type inside TypeToken (Type type = new TypeToken<CustomResponse<T>>(){}.getType();). When I change T into POJOA, I can run my app just fine to deserialize json into POJOA, but not into POJOB and POJOC.

How do I put generic type into TypeToken? Or, is it possible to do something like this:

if (T == POJOA) {
  Type type = new TypeToken<CustomResponse<POJOA>>(){}.getType();
} else if (T == POJOB) {
  Type type = new TypeToken<CustomResponse<POJOB>>(){}.getType();
} else if (T == POJOC) {
  Type type = new TypeToken<CustomResponse<POJOC>>(){}.getType();
};

Previous Question: Why do parseNetworkResponse return nothing when using parametrized type?

I suspect the error is in return (Response<T>) Response.success(gson.fromJson(json, typeOfT), HttpHeaderParser.parseCacheHeaders(response)); part, because it could print Log.d("CustomRequest", json); on previous line. (please look at My GsonRequest)

My POJO

public class CustomResponse<T> {
  T response;

  public CustomResponse(T response) {
    this.response = response;
  }

  public T getResponse() {
    return response;
  }
}

My custom deserializer

public class POJOADeserializer implements JsonDeserializer<CustomResponse<POJOA>> {

    @Override
    public CustomResponse<POJOA> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        //some deserializing thing

        return new CustomResponse<POJOA>();
    }
}

public class POJOBDeserializer implements JsonDeserializer<CustomResponse<POJOB>> {

    @Override
    public CustomResponse<POJOB> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        //some deserializing thing

        return new CustomResponse<POJOB>();
    }
}

public class POJOCDeserializer implements JsonDeserializer<CustomResponse<POJOC>> {

    @Override
    public CustomResponse<POJOC> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        //some deserializing thing

        return new CustomResponse<POJOC>();
    }
}

My GsonRequest

public class CustomRequest<T> extends JsonRequest<T> {

    private final Gson gson;
    private final Type typeOfT
    private final Response.Listener<T> listener;

    public CustomRequest(int method,
                         String url,
                         Type typeOfT,
                         JSONObject params,
                         Response.Listener<T> listener,
                         Response.ErrorListener errorListener) {

        super(method, url, params.toString(), listener, errorListener);
        this.typeOfT = typeOfT;
        this.listener = listener;

        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(new TypeToken<CustomResponse<POJOA>>(){}.getType(), new POJOADeserializer());
        gsonBuilder.registerTypeAdapter(new TypeToken<CustomResponse<POJOB>>(){}.getType(), new POJOBDeserializer());
        gsonBuilder.registerTypeAdapter(new TypeToken<CustomResponse<POJOC>>(){}.getType(), new POJOCDeserializer());
        this.gson = gsonBuilder.create();
    }

        @Override
    protected void deliverResponse(T response) {
        listener.onResponse(response);
    }

    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            Log.d("CustomRequest", json);

            return (Response<T>) Response.success(gson.fromJson(json, typeOfT), HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JsonSyntaxException e) {
            return Response.error(new ParseError(e));
        }
    }
}

The actual calling

public <T> void get(String url,
                    Response.Listener<CustomResponse<T>> listener,
                    Response.ErrorListener errorListener) {

  Type type = new TypeToken<CustomResponse<T>>(){}.getType();
  CustomRequest<CustomResponse<T>> request = new CustomRequest<>(Request.Method.GET, url, type, null, listener, errorListener);
  Volley.newRequestQueue(context.getApplicationContext()).add(request);
}


public void getResponse() {
  String url = //some url;

  get(url, new Response.Listener<CustomResponse<POJOA>>() {
    @Override
    public void onResponse(CustomResponse<POJOA> response) {
      //do something
    }
  }, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
      //do something
    }
  });
}

I can print Log.d("CustomRequest", json);

so that means I get the json response just fine.

The problem is return (Response<T>) Response.success(gson.fromJson(json, typeOfT), HttpHeaderParser.parseCacheHeaders(response));.

Is it error when casting to (Response<T>).

Or is it error on gson.fromJson(json, typeOfT)


回答1:


You have to specify the Type explicitly. There's type erasure in Java, which basically means that at runtime all instances of T are replaced with Object.

You should either write three different deserializers for different response types(POJOA, POJOB, POJOC) or write a generic one. Something like this should work:

public class GenericConverter implements JsonDeserializer<CustomResponse<?>> {
    private Type typeOfResponse;

    public GenericConverter(Type typeOfResponse) {
        this.typeOfResponse = typeOfResponse;
    }

    public CustomResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx) {
        CustomResponse response = new CustomResponse();
        response.setResponse(ctx.deserialize(json.getAsJsonObject().get("response"), typeOfResponse));
        return response;
    }
}

And register it appropriately:

gsonBuilder.registerTypeAdapter(new TypeToken<CustomResponse<POJOA>>(){}.getType(),
        new GenericConverter(POJOA.class));
gsonBuilder.registerTypeAdapter(new TypeToken<CustomResponse<POJOB>>(){}.getType(),
        new GenericConverter(POJOB.class));
gsonBuilder.registerTypeAdapter(new TypeToken<CustomResponse<POJOC>>(){}.getType(),
        new GenericConverter(POJOC.class));


来源:https://stackoverflow.com/questions/29873650/how-do-i-put-generic-type-for-gsons-typetoken

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!