如何通过静态实用程序方法正确使用Gson反序列化泛型类型的对象?

时间:2019-02-27 23:39:19

标签: java generics gson deserialization

我看过许多其他类似的问题,但我认为在这些问题上有一定程度的抽象。即,我有一个带有静态通用包装器方法的实用工具类,用于反序列化通用类型的对象(在构建时未知):

public final class Utils {

    public static final Gson sGson = new Gson();

    public static synchronized <T> T fromJson(String doc) {
        return sGson.fromJson(doc, new TypeToken<T>(){}.getType());
    }
}

一个简单的类对其进行测试:

public class TestDocument {
    public TestDocument(String test) {
        this.test = test;
    }

    public String test;
}

效果很好:

assertEquals(
   new TestDocument("test").test, 
   ((TestDocument) Utils.sGson.fromJson(
                      "{\"test\": \"test\"}", 
                      new TypeToken<TestDocument>(){}.getType())).test);

但是,尽管静态通用实用程序方法没有,但看上去却是等效的调用方式:

assertEquals(
   new TestDocument("test").test, 
   Utils.<TestDocument>fromJson("{\"test\": \"test\"}").test);

引发以下异常:

  

java.lang.ClassCastException:com.google.gson.internal.LinkedTreeMap   无法转换为TestDocument

有没有办法使它通过通用方法工作?

3 个答案:

答案 0 :(得分:1)

如果可能的话,Gson可能已经添加了此方法,并且看起来像这样:

TestDocument document = gson.<TestDocument>fromJson(json);

具有此签名的方法:

public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException

已包含JavaDoc

  

此方法将指定的Json反序列化为   指定的类别。如果指定的类是A,则不适合使用   通用类型,因为它将没有通用类型信息   由于 Java的类型清除功能。因此,这种方法   如果所需类型是泛型类型,则不应使用。注意   如果指定的任何字段都可以使用此方法   对象是泛型的,只是对象本身不应该是泛型的   类型。对于对象为通用类型的情况,请调用   fromJson(String, Type)。如果您在Reader中使用Json而不是String,请使用fromJson(Reader, Class)

第二个参数名称为classOfT的含义为the class of T

答案 1 :(得分:1)

有一些类型用法提示:

  • 由于泛型类型擦除,使用<T>而不传递实际类型是一个骗局。
  • 将类型作为Class<T>传递不是一个好主意,因为###.class仅表示JVM加载的类(原始类型除外)。这样,Class<List<String>>Class<List<Map<Integer, ?>>>完全一样,List.class失去了类型参数设置,因此在不考虑适当类型的情况下使Gson(反)序列化工作(例如,LinkedHashTreeMap是一个很好的例子,如果我记得)。
  • Gson通常与Type一起使用,它是Java类型系统可以表示的任何类型(包括类,ParameterizedType等)的超类型接口。参见https://google.github.io/gson/apidocs/com/google/gson/Gson.html#fromJson-java.lang.String-java.lang.reflect.Type-
  • TypeToken是Java中通用类型持有者的一个很好的例子,其中包括根据其生成方式产生适当的类型信息。它可以使您的方法类型安全:public static <T> T fromJson(String doc, TypeToken<? extends T> typeToken) { return sGson.fromJson(doc, typeToken.getType()); }。类型令牌可以被缓存到具有实际参数化功能的公共(是)静态最终字段中,因为它们在线程之间是不可变的并且是线程安全的。

奖金:

  • 那里不需要synchronizedGson实例也是线程安全的。

答案 2 :(得分:0)

在Java中,如果不显式传递实际类型,以致于在构建时就知道它,这是不可能的。

这是一种解决方案:

    public static synchronized <T> T fromJson(String doc, Class<T> type) {
        return sGson.fromJson(doc, type);
    }

然后测试通过:

    assertEquals((new TestDocument("test").test), Utils.<TestDocument>fromJson("{\"test\": \"test\"}", TestDocument.class).test);

在这个特定的(故意过分简化)的示例中,这看起来可以用一种更简单,更优雅的方式来完成,但是当它是更大,更复杂的场景的一部分时,它可能是唯一的选择。