如何使用 gson 处理泛型类型?

huangapple go评论65阅读模式
英文:

How to handle Generic types with gson?

问题

以下是翻译好的内容:

我创建了一个类来封装Gson的序列化和反序列化行为。思路是使这个类完全通用,以便软件中的任何部分都可以使用它。

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;
    }
}

我在反序列化的过程中遇到了问题(方法convertToString)。这行代码:T result = gson.fromJson(jsonString, type); 在编译时抛出以下错误:

“无法确定 T的类型参数;对于具有上界T、java.lang.Object的类型变量T,不存在唯一的最大实例”

我理解这意味着代码在某种程度上过于模糊和不安全。在这个版本之前,我传递的是名为object的参数,而不是变量type,但问题是返回的对象存在问题,在代码执行后会抛出NullPointerException。我阅读了关于Gson和泛型类型的问题,所以我进入了实际版本。

以防万一,这是对有问题方法的调用(response是一个字符串):

JsonParser.convertToString(response, Response.class);

所以问题是:如何使这个代码能够编译,并保持我的方法在意图上是通用的?

英文:

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, &quot;Result: {0}&quot;, result);
        return result;
    }
    
    public static &lt;T extends Object&gt; T convertToString(String jsonString, Class&lt;T&gt; object) {
        Type type = new TypeToken&lt;T&gt;() {}.getType();
        T result = gson.fromJson(jsonString, type);
        log.log(Level.INFO, &quot;Result: {0}&quot;, 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>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?

答案1

得分: 1

不要在实用方法中创建类型标记:

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

让调用者为您创建它:

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;
}

示例:

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;

输出:

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

附注:

  • 您的实用类命名为JsonParser,这与同名的类GSON库中的类冲突,可能会引起不必要的混淆。

  • 将JSON字符串反序列化为对象的方法命名为convertToString,但实际上它执行的是相反的操作。

  • 泛型类型参数<T extends Object>没有太多意义,Java中的每个对象实例都扩展自Object,因此可以简单地写成<T>

英文:

Don't create type token in your utility method:

Type type = new TypeToken&lt;T&gt;() {}.getType();

Let caller create it for you:

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

Example:

class Pojo {
    private String field1;
    private int field2;

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

List&lt;Pojo&gt; list = convertToString(&quot;[{\&quot;field1\&quot;:\&quot;abc\&quot;, \&quot;field2\&quot; : 10}]&quot;, new TypeToken&lt;List&lt;Pojo&gt;&gt;() {});

assert list.size() == 1;
assert list.get(0).field1.equals(&quot;abc&quot;);
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 &lt;T extends Object&gt; doesn't make much sense, every object instance in java extends Object, so it could be just &lt;T&gt;

huangapple
  • 本文由 发表于 2020年10月22日 02:23:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/64469521.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定