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

问题描述 投票:1回答:3

我已经看到了许多其他类似的问题,但我认为在那些产生差异的那些问题上有一定程度的抽象。也就是说,我有一个带有静态通用包装器方法的实用程序类来反序列化泛型类型的对象(在构建时未知):

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

有没有办法让它通过泛型方法工作?

java generics gson deserialization
3个回答
1
投票

如果有可能,Gson可能会添加这个方法,它可能看起来像这样:

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

使用此签名的方法:

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

包括JavaDoc

此方法将指定的Json反序列化为指定类的对象。如果指定的类是泛型类型,则不适合使用,因为由于Java的Type Erasure特性,它不具有泛型类型信息。因此,如果所需类型是泛型类型,则不应使用此方法。请注意,如果指定对象的任何字段是泛型,则此方法可以正常工作,只是对象本身不应该是泛型类型。对于对象是泛型类型的情况,请调用fromJson(String, Type)。如果你有一个qson在Reader而不是String,请使用fromJson(Reader, Class)代替。

甚至第二个参数名称是classOfT有意义the class of T


1
投票

有一些类型用法提示:

  • 使用<T>而不传递实际类型是一种恶作剧,因为通用类型擦除。
  • 将类型作为Class<T>传递并不是一个好主意,因为###.class仅表示由JVM加载的类(基本类型除外)。有了它,Class<List<String>>Class<List<Map<Integer, ?>>>是完全相同的List.class丢失类型参数化因此使Gson(de)序列化工作没有适当的类型(例如,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实例也是线程安全的。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.