Gson 泛型映射

在这篇文章中,我们将探讨 Gson 如何处理 Java 泛型。 泛型对于任何数据映射库都是一个挑战,因为它们带来了不确定性。 如果我们想查看 Java 泛型、用例和含义,请随时阅读 Wikipedia 文章。 Gson 设计得相当好,可以处理泛型,但是大家需要知道一些事情。 感兴趣的? 继续阅读!


泛型的序列化

让我们从更简单的序列化案例开始。 由于我们想将 Java 对象转换为 JSON,我们已经知道需要使用什么类型和映射,对吗?

嗯,不完全。 但是让我们开始使用最常用的泛型:Java 集合。 我们有两个列表,一个包含整数值,另一个包含字符串值。

正如在之前的文章中已经看到的,将 Java 对象转换为 JSON 通常是直接的 gson.toJson() 调用:

Gson gson = new Gson();

List<Integer> integerList = new ArrayList<>();  
integerList.add(1);  
integerList.add(2);  
integerList.add(3);

List<String> stringList = new ArrayList<>();  
stringList.add("1");  
stringList.add("2");  
stringList.add("3");

String integerJson = gson.toJson(integerList);  
String stringJson = gson.toJson(stringList);  

这通常也适用于此。 但是,不能保证一定会! 使用普通的标准 Java 类型,Gson 可以毫无问题地确定类型,但是当我们转换复杂的对象模型时,我们始终建议使用安全的 new TypeToken

Gson gson = new Gson();

List<Integer> integerList = new ArrayList<>();  
integerList.add(1);  
integerList.add(2);  
integerList.add(3);

List<String> stringList = new ArrayList<>();  
stringList.add("1");  
stringList.add("2");  
stringList.add("3");

Type integerType = new TypeToken<List<Integer>>() {}.getType();  
Type stringType = new TypeToken<List<String>>() {}.getType();

String integerJson = gson.toJson(integerList, integerType);  
String stringJson = gson.toJson(stringList, stringType);  

new TypeToken 调用通过使用一个空的匿名内部类来创建一个类型文字。 这就是为什么该行看起来有点时髦并以 (){} 结尾的原因。

尽管如此,这将确保 Gson 知道正确的泛型类型,并且我们将得到完整的 JSON:

Gson 泛型映射

再一次,对于很多简单的情况,比如上面的那个,这不是必需的。 我们建议大家习惯使用 TypeToken 方法,以避免最终遇到的问题。

在查看反序列化之前,让我们再回顾一个 Java 集合之外的泛型示例。 我们的 Box 类非常简单,只包含一个在编译时未知类型的对象:

public class Box<T> {  
    private T boxContent;

    public Box(T t) {
        this.boxContent = t;
    }
}

我们整洁的 Box 类可以处理我们扔给它的任何类型的对象类型。 正如我们上面提到的,如果想安全起见,即使在序列化期间也要使用 new TypeToken 方法。

Gson gson = new Gson();

Box<String> stringBox = new Box<>("String Type");  
Box<Integer> integerBox = new Box<>(42);  

Box<UserSimple> complexBox = new Box<>(new UserSimple("jiyik", "jiyik_onmpw@163.com", 26, true));

Type stringType = new TypeToken<Box<String>>() {}.getType();  
Type integerType = new TypeToken<Box<Integer>>() {}.getType();  
Type complexType = new TypeToken<Box<UserSimple>>() {}.getType();

String integerJson = gson.toJson(integerBox, integerType);  
String stringJson = gson.toJson(stringBox, stringType);  
String complexJson = gson.toJson(complexBox, complexType);  

这将为创建的 Java 对象生成完美的 JSON 匹配。

我们保证,在反序列化过程中它会变得更有趣! 我们将在下一节中深入探讨细节。


泛型反序列化

假设我们从我们的 API 接收 JSON,它使用 Java 泛型。 例如,使用上一节中的 Box 类,我们可以期待以下 JSON:

{
  "boxContent": {
    "_name": "jiyik",
    "age": 26,
    "email": "jiyik_onmpw@163.com",
    "isDeveloper": true,
    "registerDate": "Jun 7, 2021 7:15:29 AM"
  }
}

这个 JSON 很好地向我们展示了核心问题是什么。

Gson 需要知道 Box 的类型是什么,否则无法映射 JSON。 例如,如果 JSON 包含 Box<String>,则它是一个完全不同的映射。

因此,我们需要在反序列化泛型时指定 Box 类型。 当然,我们为此使用 TypeToken 类。

String complexGenericJson = "{'boxContent':{'_name':'jiyik','age':26,'email':'jiyik_onmpw@163.com','isDeveloper':true,'registerDate':'Jun 7, 2021 7:15:29 AM'}}";

Type complexType = new TypeToken<Box<UserDate>>() {}.getType();

Gson gson = new Gson();  
Box boxWithData = gson.fromJson(complexGenericJson, complexType);  
Box<UserDate> boxWithoutData = gson.fromJson(complexGenericJson, Box.class);  

只要我们可以非常具体地了解传入类型,即使它包含在一些 Java 泛型中,Gson 也应该能够映射它。