为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

慕哥6287543 2022-07-14 10:35:47
我见过许多其他类似的问题,但我认为在这些问题之上还有一定程度的抽象会有所不同。即,我有一个带有静态泛型包装方法的实用程序类来反序列化泛型类型的对象(在构建时未知):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 回答

?
江户川乱折腾

TA贡献1851条经验 获得超5个赞

有一些类型使用提示:

  • <T>由于泛型类型被擦除,在不传递实际类型的情况下使用是一个骗局。

  • 传递类型 asClass<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实例也是线程安全的。


查看完整回答
反对 回复 2022-07-14
?
一只萌萌小番薯

TA贡献1795条经验 获得超7个赞

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

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

具有此签名的方法:

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

包括JavaDoc

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

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


查看完整回答
反对 回复 2022-07-14
?
婷婷同学_

TA贡献1844条经验 获得超8个赞

看起来如果不显式传递实际类型以便在构建时知道它,就不可能在 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);

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


查看完整回答
反对 回复 2022-07-14
  • 3 回答
  • 0 关注
  • 227 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号