原文鏈接:Gson — Mapping of Arrays and Lists of Objects
原文出自:Norman Peitek
譯者:無名無
歡迎到我們的 Gson 系列的另一篇文章,在回顧了 Gson 的基礎用法之後,例如:模型註解和嵌套對象的映射,我們來討論本文重點功能:數組和列表對象的映射,我們幾乎都會使用到這種數據類型,幸運的是,Gson 可以幫我們輕鬆搞定。
Array 和 List 差異
在我們介紹序列化之前,我們先來看下 Java 中的兩種數據結構:Array 和 List。在 Java 中兩者實現方式不同,使用哪一種數據類型取決於你的實際需求,但是在序列化這個問題上, Gson 並不關心這兩種數據結構的具體實現。
在 JSON 數據格式中,不存在數組等結構,只是 Java 的具體實現使得這兩種數據類型有很大不同。但是在上層它們表示出相同的結構,接下來,我們將重新理解這兩種數據類型,在看過幾個例子之後你就懂了。
Array 和 List 序列化
還記得上一篇關於 restaurant model 的嵌套對象嗎?接下來,我們爲restaurant 添加一個 menu 屬性,包含兩個字段,restaurant 中的菜單可以理解成一個 restaurant 列表。如下:
提示:我們這裏只是簡單的舉個菜單的例子,這個數據結構不是一個完整的數據,所以它是不能被真實使用的。
首先要創建一個 Java model 類:
public class RestaurantWithMenu {
String name;
List<RestaurantMenuItem> menu;
//RestaurantMenuItem[] menu; // alternative, either one is fine
}
public class RestaurantMenuItem {
String description;
float price;
}
通過嵌套對象的方式即可,在 Java Model 中包含要映射變量的引用就可以了,要注意名字和JSON中字段名相同。
JSON 格式如下:
{
"name": "Future Studio Steak House",
"menu": [
...
]
}
與嵌套對象類似,我們沒有 menu 的直接值,相反,JSON 中通過 “[]” 來包含一個對象,如上所述,在 JSON 數據中,數組和 List 結構是沒有區別的。
menu 中包含一些對象,在我們那的 model 中,menu 只是其中的一個變量,我們先來手動生成一個完整的 JSON 數據。
通過下面這種方式,我們來模擬一個完整的 restaurant 數據:
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("Future Studio Steak House", menu);
Gson gson = new Gson();
String restaurantJson = gson.toJson(restaurant);
生成JSON如下:
{
"menu": [
{
"description": "Spaghetti",
"price": 7.99
},
{
"description": "Steak",
"price": 12.99
},
{
"description": "Salad",
"price": 5.99
}
],
"name": "Future Studio Steak House"
}
如我們預料,我們得到了想要的數據,按照字母順序,menu 排在了name 的前面,根據 “[]” 標誌 List 開始,根據 “{}” 標誌對象開始。
但是我們並不是總是將 List 嵌套在對象中,我們可能會直接得到一個 List,Gson 也是支持直接序列化一個 List。
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 中的 “[” 表示一個對象列表開始,”{“表示一個對象開始了,我們應該記住 JSON 數據中格式差別。
數組和 List 反序列化
在第二部分我們將學習反序列化,我們將使用 Gson 來解析列表數據,在之前的例子中,我們列舉了兩種情況,一是列表作爲跟節點,二是列表最爲一個嵌套對象。
List作爲跟節點
來看一個列表最爲根節點的例子:
[
{
"name": "Christian",
"flowerCount": 1
},
{
"name": "Marcus",
"flowerCount": 3
},
{
"name": "Norman",
"flowerCount": 2
}
]
根據之前介紹的,”[]”標示一個GSON 解析列表的開始和結束,我們還需要一個具體的 Java model類:
public class Founder {
String name;
int flowerCount;
}
接下來,我們將數據解析成我們想要的數據類型。
數組:
首先看解析成數組,通過 Gson 的 gson.fromJson 方法,我們很簡單的將 GSON 解析成數組,注意這裏傳遞的參數是 Founder[].class 而不是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);
Debug 如下:
Lists
實際開發中,我們更多的是轉成一個 ArrayList,但是,我們不能像解析數組那樣傳入 List,爲了讓Gson知道我們要解析的數據類型,我們必須傳遞給它一個Type,內部根據 TypeToken 來區分要解析的類型。例如:
Type founderListType = new TypeToken<ArrayList<Founder>>(){}.getType();
完成解析如下:
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);
Debug如下:
以上兩種方式,根據自己實際需求選擇。接下來來看看反序列化操作。
### 列表作爲對象的一部分
我們現在有一個這樣的數據:
{
"name": "Future Studio Dev Team",
"website": "https://futurestud.io",
"founders": [{
"name": "Christian",
"flowerCount": 1
}, {
"name": "Marcus",
"flowerCount": 3
}, {
"name": "Norman",
"flowerCount": 2
}]
}
老樣子我們需要創建一個用來對應的 Java Model 類:
public class GeneralInfo {
String name;
String website;
List<Founder> founders;
}
列表存在 Model 類中的一個好處就是,我們在使用Gson解析時不再需要傳遞 TypeToken,直接穿入類即可。
String generalInfoJson = "{'name': 'Future Studio Dev Team', 'website': 'https://futurestud.io', 'founders': [{'name': 'Christian', 'flowerCount': 1 }, {'name': 'Marcus','flowerCount': 3 }, {'name': 'Norman','flowerCount': 2 }]}";
Gson gson = new Gson();
GeneralInfo generalInfoObject = gson.fromJson(generalInfoJson, GeneralInfo.class);
Debug如下:
除了解析成一個 List,也可以解析爲數組格式。
List 中嵌套 List
Gson 也可以解析 List 中嵌套 List 數據結構,看下面這個例子,例如:
public class GeneralInfo {
String name;
String website;
List<FounderWithPets> founders;
}
public class FounderWithPets {
String name;
int flowerCount;
List<Pet> pets;
}
public class Pet {
String name;
List<Toy> toys;
}
JSON 中包含了三個 List,這裏就不操作了,相信 Gson 也是可以解析的。只要我們將需要解析的類型傳遞正確就可以。
目標
本文你將瞭解如何使用Gson 來序列化和反序列化 ArrayList 和 數組,知道了如何根據 JSON 格式中的不同來判斷是對象還是一個對象集合。
你可以通過評論或twitter @futurestud_io 反饋你的問題。
練習代碼已上傳 Github https://github.com/whiskeyfei/Gson-Review 可自行查看。
Gson 系列文章翻譯回顧
1、Gson - Java-JSON 序列化和反序列化入門
2、Gson - 映射嵌套對象
3、Gson - Arrays 和 Lists 映射對象
4、Gson - Map 結構映射
5、Gson - Set 集合映射
6、Gson - 空值映射
7、Gson Model Annotations - 如何使用 @SerializedName 更改字段的命名
8、Gson Model Annotations - @SerializedName 匹配多個反序列化名稱
9、Gson Builder - 基礎和命名規則
10、Gson Builder - 序列化空值
11、Gson Builder - 忽略策略
12、Gson Builder - Gson Lenient 屬性
13、Gson Builder - 特殊類型 Floats & Doubles
17、Gson Builder - 如何使用 @Expose 忽略字段
19、Gson Advanced - 映射枚舉類型
20、Gson Advanced - 映射循環引用
21、Gson Advanced - 泛型
22、Gson Advanced - 簡單自定義序列化 (Part 1)
24、Gson Advanced - 自定義反序列化基礎
25、Gson Advanced - 自定義對象實例創建
26、Gson Advanced - 通過 @JsonAdapter 自定義(反)序列化過程
32、Practical Gson - 如何解析多態對象