Gson — 数组和对象列表的映射
欢迎回到我们 Gson 系列的另一篇文章。 在之前的几篇文章中我们介绍了 Gson 的基础知识、模型注释和嵌套对象的映射,现在我们将继续讨论一个核心特性:数组和列表的映射。 几乎每个数据模型都使用某种形式的列表。 幸运的是,Gson 可以很容易地处理它们。
数组和列表之间的区别
在我们进入特定的(反)序列化示例之前,我们要检查两个 Java 结构 Arrays 和 Lists。 Java 实现有很大的不同,任何一种都有其优点。 我们将在用例中应用什么取决于软件要求,至少部分取决于我们自己的个人品味。 将列表或数组结构映射到 JSON 时,有趣的是:这与他们是数组还是列表并没有关系。
在 JSON 数据格式中,没有列表或数组。 是的,Java 实现在它们之间产生了巨大的差异,但在高层次上,它们以列表形式表示完全相同的数据。 在博客文章的其余部分,我们将它们命名为对象列表,但在 Java 端它们可以是任何一种。 如果这有点令人困惑,请不要担心,在几个例子之后会变的清楚。
数组/列表的数据序列化
还记得上一篇关于嵌套对象的博客文章中的模型吗? 是时候给餐厅添加菜单了,不是吗? 我们想知道以什么价格提供什么美味的食物。 餐厅菜单可以构造为菜单项列表。 一件商品正是客户可以订购的一种选择。 在我们简单的 restaurant
中,每件商品都有 description 和 price 。
提示:我们将尽量保持结构简单,不处理菜单类别、组合或配菜。 这显然不是一个完整的模型,所以不要将它用于实际的商业餐厅应用程序......
如果我们考虑具体的 Java 模型实现,我们会得到类似于下面的代码:
public class RestaurantWithMenu {
String name;
List<RestaurantMenuItem> menu;
//RestaurantMenuItem[] menu; // 另一种选择,任何一个都可以
}
public class RestaurantMenuItem {
String description;
float price;
}
只是对于嵌套对象,Java 处理对象的方式与 JSON 不同。 Java 可以将它们保留为单独的类,并且只保留对 List 或 Array 实现的引用。 JSON 需要将列表保存为本地嵌套列表。 这意味着在高层次上,我们期望在 JSON 方面是这样的:
{
"name": "迹忆客美食馆",
"menu": [
...
]
}
与嵌套对象类似,我们没有直接的 menu 值。 相反,JSON 通过用 []
包装值来声明即将到来的对象列表。 如上所述,这是数组还是列表没有区别。 在 JSON 数据结构中,它看起来是一样的。
menu 的内容是一堆对象。 在我们的例子中,它们是餐厅的菜单项。 让我们运行 Gson 来看看完整的 JSON 是什么样子。
我们希望大家现在已经知道这个练习了。 获取 Java 对象,初始化 Gson,然后让 Gson 创建匹配的 JSON:
List<RestaurantMenuItem> menu = new ArrayList<>();
menu.add(new RestaurantMenuItem("Spaghetti", 7.99f));
menu.add(new RestaurantMenuItem("Steak", 12.99f));
menu.add(new RestaurantMenuItem("Salad", 5.99f));
RestaurantWithMenu restaurant =
new RestaurantWithMenu("迹忆客美食馆", menu);
Gson gson = new Gson();
String restaurantJson = gson.toJson(restaurant);
restaurantJson
包含以下内容:
{
"name": "迹忆客美食馆",
"menu": [
{
"description": "Spaghetti",
"price": 7.99
},
{
"description": "Steak",
"price": 12.99
},
{
"description": "Salad",
"price": 5.99
}
]
}
该列表(由 […]
表示)包含多个对象(每个由 {…}
表示)。
但是我们并不总是发送嵌套在单个对象中的数据列表,就像我们在上面所做的那样。 有时我们只想发送一个列表。 当然,Gson 也支持列表的 JSON 序列化。 例如,如果我们只是像这样序列化菜单项:
List<RestaurantMenuItem> menu = new ArrayList<>();
menu.add(new RestaurantMenuItem("Spaghetti", 7.99f));
menu.add(new RestaurantMenuItem("Steak", 12.99f));
menu.add(new RestaurantMenuItem("Salad", 5.99f));
Gson gson = new Gson();
String menuJson = gson.toJson(menu);
这将导致以下的结果:
[
{
"description": "Spaghetti",
"price": 7.99
},
{
"description": "Steak",
"price": 12.99
},
{
"description": "Salad",
"price": 5.99
}
]
让我们指出关键的区别:JSON 的第一个字符是 [
,表示即将到来的对象列表! 到目前为止,我们只查看了以 {
开头的对象。 我们应该立即尝试记住这种差异。 如果大家继续阅读,那么我们将在下一节中一直需要它。
数组/列表的数据反序列化
在第二部分中,我们将完成反序列化。 换句话说,我们如何使用 Gson 从 JSON 结构中的列表映射到 Java 对象。 在前面的示例中,我们已经研究了列表是根还是嵌套在 JSON 数据中的对象中的重要区别。
根对象列表
所以让我们做一个练习。 如果 迹忆客 将启动我们自己的 API,我们还将提供一个端点 GET /founders
。 这个端点将返回餐厅里的三个人的名字和一个flowerCount,它显示了我们桌子上的植物数量。 那么下面的 JSON 是否有一个作为 root 的列表?
[
{
"name": "Christian",
"flowerCount": 1
},
{
"name": "Marcus",
"flowerCount": 3
},
{
"name": "Norman",
"flowerCount": 2
}
]
是的,你是对的。 虚构的 GET /founders
端点直接返回一个列表。 JSON 以 []
开头和结尾。 你也说得对,Marcus 喜欢在办公桌的绿色丛林中工作。
那么我们如何将 Gson 映射到 Java 对象呢? 第一步是创建我们的模型:
public class Founder {
String name;
int flowerCount;
}
第二步取决于你。 你是想使用列表或数组作为自己的类型吗?
数组
如果你想使用数组,这很简单。 可以像我们之前所做的那样直接使用 fromJson()
函数,并将模型类作为数组传递,例如 gson.fromJson(founderGson, Founder[].class);
。
String founderJson = "[{'name': 'Christian','flowerCount': 1}, {'name': 'Marcus', 'flowerCount': 3}, {'name': 'Norman', 'flowerCount': 2}]";
Gson gson = new Gson();
Founder[] founderArray = gson.fromJson(founderJson, Founder[].class);
这将创建一个创建者对象的 Java 数组,这些对象具有正确映射的属性
列表
由于包含有更多的方法,现在许多开发人员更喜欢 Java 列表。 不幸的是,我们不能直接将 List<Founder>
传递给 Gson。 为了让 Gson 正确理解 List 结构,你必须弄清楚它的 Type。 幸运的是,有一个 Gson 类 TypeToken
可以帮助我们为几乎任何类配置找到正确的 Type 。 对于 ArrayList
中的 Founder
类,它看起来像这样:
Type founderListType = new TypeToken<ArrayList<Founder>>(){}.getType();
我们可以将语句的结果用作 Gson 调用的类型:
String founderJson = "[{'name': 'Christian','flowerCount': 1}, {'name': 'Marcus', 'flowerCount': 3}, {'name': 'Norman', 'flowerCount': 2}]";
Gson gson = new Gson();
Type founderListType = new TypeToken<ArrayList<Founder>>(){}.getType();
List<Founder> founderList = gson.fromJson(founderJson, founderListType);
这与 Array 方法一样有效:
最后,如果我们将数据映射到数组或列表,这取决于大家的个人喜好和用例。 让我们再看一个主题:如何反序列化嵌套在对象中的列表:
作为对象的一部分的列表
我们用另一个通用端点 GET /info
扩展了我们想象中的 迹忆客 API 。 它返回的不仅仅是 Founder 信息:
{
"name": "迹忆客美食馆m",
"website": "https://www.jiyik.com",
"founders": [
{
"name": "Christian",
"flowerCount": 1
},
{
"name": "Marcus",
"flowerCount": 3
},
{
"name": "Norman",
"flowerCount": 2
}
]
}
首先,我们必须为 JSON 响应创建一个合适的模型。 我们可以重用上一节中的 Founder
类:
public class GeneralInfo {
String name;
String website;
List<Founder> founders;
}
嵌套在对象中的列表的优点是简单的 Gson 调用,无需任何 TypeToken
处理。 我们可以直接传类:
String generalInfoJson = "{'name': '迹忆客美食馆', 'website': 'https://www.jiyik.com', 'founders': [{'name': 'Christian', 'flowerCount': 1 }, {'name': 'Marcus','flowerCount': 3 }, {'name': 'Norman','flowerCount': 2 }]}";
Gson gson = new Gson();
GeneralInfo generalInfoObject = gson.fromJson(generalInfoJson, GeneralInfo.class);
这将产生一个完整的对象:
当然,我们可以将 GeneralInfo
类更改为使用 Founder[]
数组而不是 List<Founder>
。 Gson 调用将保持不变。
注意
:不知道大家是否注意到 Gson 对具有 name 属性的 GeneralInfo 和 Founder 模型都没有问题? 它会毫无问题地对其进行序列化和反序列化。 这是多么惊人!
嵌套在列表中的列表
Gson 对嵌套在列表中的列表是也正常映射。 例如,以下模型没有问题:
public class GeneralInfo {
String name;
String website;
List<FounderWithPets> founders;
}
FounderWithPets
类现在稍微扩展了一个宠物列表:
public class FounderWithPets {
String name;
int flowerCount;
List<Pet> pets;
}
另一方面, Pet
类包括一系列玩具:
public class Pet {
String name;
List<Toy> toys;
}
Toy
类包括……好吧,我们现在停止。 我们希望这能说明这一点:我们可以将列表包装在列表中(甚至不止一次)而不会出现任何问题。 Gson 在处理序列化和反序列化方面简直就是一个世界冠军。
尽管如此,Gson 只能处理具有一定一致性的对象列表。 如果对象是完全随机和任意的,Gson 将无法映射它们! 不过,多态对象列表不是问题。
在这篇博文中,我们了解了 Gson 如何能够毫无问题地映射列表(或数组)数据。 Gson 很灵活,在 Java 端接受 List 和 Array 实现。 我们需要学习如何从 JSON 结构中识别列表是根还是嵌套在对象中。 我们还学习了如何设置从 JSON 到 Java 数组或 Java 列表的不同反序列化。
相关文章
Gson 通过@JsonAdapter 自定义(反)序列化
发布时间:2022/07/24 浏览次数:217 分类:编程语言
-
在这篇文章中,我们将展示如何简化(反)序列化的自定义。 所有这些选项都只能通过自定义 Gson 实例和一些样板代码获得。 Gson 2.7 引入了一个简单的注解,我们可以节省大量代码并获
Gson 自定义反序列化基础
发布时间:2022/07/21 浏览次数:81 分类:编程语言
-
在这篇文章中,我们将了解如何实现自定义 Gson 反序列化。 如果服务器以与客户端的应用程序数据模型不匹配的格式向我们发送数据,请继续阅读!
Gson 自定义实例创建器
发布时间:2022/07/14 浏览次数:121 分类:编程语言
-
在这篇文章中,我们将讨论自定义反序列化的另一个组件。 在过去的几篇文章中,我们探讨了如何自定义数据的序列化和反序列化。 在这两种情况下,我们都试图减轻服务器和客户端之
Gson 循环引用的映射
发布时间:2022/07/13 浏览次数:173 分类:编程语言
-
在这篇文章中,我们将讨论一个特别讨厌的话题:循环引用。 我们可能在计算机科学或图表数学课上听说过循环引用。 在更实际的解释中:它处理对象具有指向不同对象的嵌套属性的情
Gson Builder Floats 和 Doubles 的特殊值
发布时间:2022/07/12 浏览次数:193 分类:编程语言
-
在上一篇 Gson 的文章中,我们研究了使 JSON 转换降低标准的选项。 Lenient 允许传入的 JSON 在某种程度上是非标准的,Gson 仍然能够将其解析为 Java 对象。 在这篇文章中,我们将研究一个
Gson 自定义序列化
发布时间:2022/07/11 浏览次数:117 分类:编程语言
-
在这篇文章中,我们将探讨如何自定义 Java 对象的 Gson 序列化。 我们可能想要更改序列化的原因有很多,例如 简化我们的模型以减少发送的数据量或删除个人信息。 现在我们将通过实
Gson 映射 Enum 枚举
发布时间:2022/07/10 浏览次数:156 分类:编程语言
-
在之前的文章中我们介绍了如何映射嵌套对象、数组和列表、Java Map 等。 在这篇文章中,您将学习如何(反)序列化 Java 枚举 Enum。
Gson 如何反序列化多态对象列表
发布时间:2022/07/09 浏览次数:286 分类:编程语言
-
最近,我们遇到了一种情况,我从 REST 端点接收到 JSON 格式的对象列表。 到目前为止,这没有什么不寻常的,也不是问题。 然而,问题是对象是多态的,需要解析子类特定的字段。 以