Edgar Hernandez
Edgar Hernandez

Reputation: 1

How to handle Generic types with gson?

I created a class which encapsulates the gson serialize/deserialize behaviors. The idea is to make the class totally generic, so this can be used by any part of the software that needs it.

public class JsonParser {
    private static final Logger log = Logger.getLogger(JsonParser.class.getName());
    
    private static final Gson gson = new Gson();
    
    public static String convertToJson(Object object) {
        String result = gson.toJson(object);
        log.log(Level.INFO, "Result: {0}", result);
        return result;
    }
    
    public static <T extends Object> T convertToString(String jsonString, Class<T> object) {
        Type type = new TypeToken<T>() {}.getType();
        T result = gson.fromJson(jsonString, type);
        log.log(Level.INFO, "Result: {0}", result.toString());
        return result;
    }
}

I'm having a problem in the deserialization (method convertToString). The line: T result = gson.fromJson(jsonString, type); is throwing this error at compile time:

"type parameters of T cannot be determined; no unique maximal instance exists for type variable T with upper bounds T,java.lang.Object"

I understand this means the code is too vague and unsafe in resume. Before this version I was passing the argument named object instead of the variable type but the problem was that the returned object had problems and threw NullPointerException later in the code's execution. I readed about the problems with Gson and Generic Types so I got into the actual version.

Just in case, this is the call to the problematic method (response is a String):

JsonParser.convertToString(response, Response.class);

So the question is: How can I make this compile and keep my method generic in the intent?

Upvotes: 0

Views: 168

Answers (1)

rkosegi
rkosegi

Reputation: 14678

Don't create type token in your utility method:

Type type = new TypeToken<T>() {}.getType();

Let caller create it for you:

public static <T> T convertToString(String jsonString, TypeToken<T> typeToken) 
    T result = gson.fromJson(jsonString, typeToken.getType());
    log.log(Level.INFO, "Result: {0}", result.toString());
    return result;
}

Example:

class Pojo {
    private String field1;
    private int field2;

    @Override
    public String toString() {
        return "Pojo [field1=" + field1 + ", field2=" + field2 + "]";
    }
}

List<Pojo> list = convertToString("[{\"field1\":\"abc\", \"field2\" : 10}]", new TypeToken<List<Pojo>>() {});

assert list.size() == 1;
assert list.get(0).field1.equals("abc");
assert list.get(0).field2 == 10;

output

INFO: Result: [Pojo [field1=abc, field2=10]]

Side notes:

  • your utility class is named JsonParser, which collides with class with same name from GSON lib - that can create unnecessary confusion

  • your method to deserialize json string into object is named convertToString, but it is doing exact opposite

  • Generic type argument <T extends Object> doesn't make much sense, every object instance in java extends Object, so it could be just <T>

Upvotes: 1

Related Questions